diff --git a/.classpath b/.classpath deleted file mode 100644 index 364e90fc..00000000 --- a/.classpath +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.gradle/2.0/taskArtifacts/cache.properties.lock b/.gradle/2.0/taskArtifacts/cache.properties.lock index 13cfa125..d132ebd2 100644 Binary files a/.gradle/2.0/taskArtifacts/cache.properties.lock and b/.gradle/2.0/taskArtifacts/cache.properties.lock differ diff --git a/.gradle/2.0/taskArtifacts/fileHashes.bin b/.gradle/2.0/taskArtifacts/fileHashes.bin index 1465e826..0dd0f16c 100644 Binary files a/.gradle/2.0/taskArtifacts/fileHashes.bin and b/.gradle/2.0/taskArtifacts/fileHashes.bin differ diff --git a/.gradle/2.0/taskArtifacts/fileSnapshots.bin b/.gradle/2.0/taskArtifacts/fileSnapshots.bin index 0afd427a..4f86e50b 100644 Binary files a/.gradle/2.0/taskArtifacts/fileSnapshots.bin and b/.gradle/2.0/taskArtifacts/fileSnapshots.bin differ diff --git a/.gradle/2.0/taskArtifacts/outputFileStates.bin b/.gradle/2.0/taskArtifacts/outputFileStates.bin index 50fc9698..fc97cae3 100644 Binary files a/.gradle/2.0/taskArtifacts/outputFileStates.bin and b/.gradle/2.0/taskArtifacts/outputFileStates.bin differ diff --git a/.gradle/2.0/taskArtifacts/taskArtifacts.bin b/.gradle/2.0/taskArtifacts/taskArtifacts.bin index 1554a442..4485af6b 100644 Binary files a/.gradle/2.0/taskArtifacts/taskArtifacts.bin and b/.gradle/2.0/taskArtifacts/taskArtifacts.bin differ diff --git a/.gradle/gradle.log b/.gradle/gradle.log deleted file mode 100644 index 8cf94dd0..00000000 --- a/.gradle/gradle.log +++ /dev/null @@ -1,41 +0,0 @@ -**************************** - Powered By MCP: - http://mcp.ocean-labs.de/ - Searge, ProfMobius, Fesh0r, - R4wk, ZeuX, IngisKahn, bspkrs - MCP Data version : unknown -**************************** -:downloadClient SKIPPED -:downloadServer SKIPPED -:getVersionJson -:extractUserDev UP-TO-DATE -:mergeJars SKIPPED -:applyBinPatches SKIPPED -:downloadMcpTools SKIPPED -:extractMcpData UP-TO-DATE -:genSrgs SKIPPED -:deobfBinJar SKIPPED -:compileApiJava UP-TO-DATE -:processApiResources UP-TO-DATE -:apiClasses UP-TO-DATE -:sourceMainJava -:compileJavawarning: [options] bootstrap class path not set in conjunction with -source 1.6 -Note: /home/seth/Downloads/serversync/build/sources/java/com/superzanti/serversync/SyncClientConnection.java uses unchecked or unsafe operations. -Note: Recompile with -Xlint:unchecked for details. -1 warning - -:processResources UP-TO-DATE -:classes -:jar -:compileTestJava UP-TO-DATE -:processTestResources UP-TO-DATE -:testClasses UP-TO-DATE -:test UP-TO-DATE -:reobf -:assemble -:check UP-TO-DATE -:build - -BUILD SUCCESSFUL - -Total time: 13.293 secs diff --git a/README-fml.txt b/README-fml.txt deleted file mode 100644 index 744ba3b6..00000000 --- a/README-fml.txt +++ /dev/null @@ -1,37 +0,0 @@ -------------------------------------------- -Source installation information for modders -------------------------------------------- -This code follows the Minecraft Forge installation methodology. It will apply -some small patches to the vanilla MCP source code, giving you and it access -to some of the data and functions you need to build a successful mod. - -Note also that the patches are built against "unrenamed" MCP source code (aka -srgnames) - this means that you will not be able to read them directly against -normal code. - -Source pack installation information: - -Standalone source installation -============================== - -To install this source code for development purposes, extract this zip file. -It ships with a demonstration mod. Run 'gradlew setupDevWorkspace' to create -a gradle environment primed with FML. Run 'gradlew eclipse' or 'gradlew idea' to -create an IDE workspace of your choice. -Refer to ForgeGradle for more information about the gradle environment -Note: On macs or linux you run the './gradlew.sh' instead of 'gradlew' - -Forge source installation -========================= -MinecraftForge ships with this code and installs it as part of the forge -installation process, no further action is required on your part. - -For reference this is version @MAJOR@.@MINOR@.@REV@.@BUILD@ of FML -for Minecraft version @MCVERSION@. - -LexManos' Install Video -======================= -https://www.youtube.com/watch?v=8VEdtQLuLO0&feature=youtu.be - -For more details update more often refer to the Forge Forums: -http://www.minecraftforge.net/forum/index.php/topic,14048.0.html diff --git a/README.md b/README.md index 6dc1820d..25ef308e 100644 --- a/README.md +++ b/README.md @@ -1,62 +1,78 @@ ServerSync ========= +This is an open source mod that allows for easy server management. The client will always be able to connect to your server and you will never again have to send them the new files and tell them to update. This method avoids a lot of complaining. As a server admin constantly changing configs/updating mods it can get to be quite a pain pushing these updates. Some users have trouble finding the minecraft folder let alone putting mods in the right place. -This is an open source mod that allows for easy server management. It simply syncs the mods folder and the config folder from the server to the client. The client will always be able to connect to your server and you will never again have to send them the new files and tell them to update. My method avoids a lot of complaining. I have a server which runs all of my own mods, as a developer I'm constatly updating these mods, fixing bugs, and making changes to configs. For the past fiew years I've been going around to the users on my server with a USB stick instructing them on how to use mods. Recently I've had some people join my server that don't know how to find the .minecraft folder. So explaining this to them and getting them the new updates can be a real pain. +Currently you can sync: +* Client-side mods +* Mods +* Flans content packs +* Configs -**Currently for Minecraft 1.7.10 Forge 1403** +**Currently for Minecraft 1.7.10 Forge 1614+** -If you don't feel like compiling from source and simply want to download the jar file, navigate around this repo's folders. If you're a developer you'll be able to find it easy. Please read the disclaimer before downloading. +If you don't feel like compiling from source and simply want to download a jar file see the releases tab, I update these periodically when theres a large enough addition/change. Please read the disclaimer before downloading. + +https://jenkins.dir.ac/job/serversync/ - Jenkins hosted by the lovely Robert Perry DISCLAIMER: ----------- +This mod is only meant for personal use or for developers that constantly push their OWN mods. Other developers work very hard on their mods and simply visiting their website, forum post, or github is just a common courtesy. Please don't use this to distribute other people's mods. -This mod is only meant to be used for developers that constantly push their OWN mods. Other developers work very hard on their mods and simply visiting their website, forum post, or github is just a common courtesy. Please don't use this to distribute other people's mods. - -Not to mention, depending on the copyright and/or pattent laws in your area using my mod with other developer's mods could be ILLEGAL. +Depending on the copyright and/or pattent laws in your area using this mod with other developer's mods for a commercial purpose could be ILLEGAL. -Don't trust anyone with my mod. This mod allows ANY server running it to put ANY jar file in your mods folder. Any mod means any function of java, such as making a virus or a keylogger. So if you are a client please make sure you trust your server administrator. +Don't trust anyone with this mod. This mod allows ANY server running it to put ANY jar file in your mods folder. Any mod means any function of java, such as making a virus or a keylogger. So if you are a client please make sure you trust your server administrator. RECENT UPDATES: ----------- -Version 2.3: -* Fixed deletion bugs - * Files couldn't be deleted because they were in use. Now I delete them when the JVM exits -* Fixed updating bugs -* Changed error messages +Version 2.5.2 +* Fixed missing class JsonReader should work properly now +* Fixed minor issue with file deletion + +Version 2.5.1 +* Fixed bug when interacting with zip/jar files + +Version 2.5.0 +* Added more functionality to the GUI +* Changed Ignoring rules to whitelist for configs +Version 2.4.7 +* Added support for flans mod content packs -Version 2.1: -* Added feature to ask the server if there has been any updates before ever updating - * This allows to update all configs regardless of if they change - * Has almost made the IGNORE_LIST property worthless, but is stil handy to have for client/server side only things like GLSL shaders +Version 2.4.6 +* Added the ability to push client-only mods +* Various code optimization/cleanup +Version 2.4.5 +* Added the ability to sync files without opening minecraft +* Fixed windows deletion issue, not an issue if using "offline" sync +* Added GUI for "offline" mode FREQUENTLY ASKED QUESTIONS: ----------- * "This mod isn't doing anything!" - * I require you to have CustomMainMenu. Create a main menu with a button that runs this mod. Use the buttonid of that button in the config file. + * This version of serversync is run independant of minecraft (minecraft should be closed when running serversync), I did this as minecraft did not need to be running for the program to work and the previous method required you to open and close minecraft several times which if you have any more than 2-3 mods then loading time gets very taxing. + * Run serversync.jar from your mods folder or create a shortcut, serversync will auto populate server details from the config if present * "I can't connect to my server" - * Did you check that the config file was the same between both the client and the server? - * Everything in the config file MUST be the same on both sides. + * Did you check that the config files ip/port details are correct on both server and client * Are you using your external/internal IP address apropriately? * Are you trying to transfer a file larger than your max file size? * Are you ignoring a file that is neccecary to connect to the server? * "This is so insecure I hate it!" - * Go read the disclaimer. It will always be insecure and I don't plan on making it super secure. + * As per the disclaimer this is not intended to be a super secure system, it's more for personal use. Want to play with your kids/partner but you dont feel like teaching all of them exactly how to update mods. * The config file allows you to put in your own hashes for the server client commands. This would take a real genious to pull files off the server or send files to you. * "Can you add feature X? Or fix bug Y?" * I don't know. Go submit it to the issues and I'll check it out. * "You're a horrible programmer" - * I'm an Electrical Engineer not a computer scientist. Please submit a bug report and help me improve. + * You're entitled to that opinion. * "Can you make this work without using a custom main menu?" - * I'm not sure. I haven't looked into it, but if you have an informed suggestion please let me know and I'll do what I can. + * This fork of serversync is run without opening minecraft and in fact is no longer supporting the in-game feature of the previous incarnation. * "Why does this mod spit out so much 'junk' in my console?" - * It's simply to help users know that they're not being attacked. It will tell them what IP they're connected to, what mod is being downloaded and more. My hope is that people will actually see this while it's running to know for sure that they can trust their admin. Hey, not everyone reads this. + * It's simply to help users know that they're not being attacked. It will tell them what IP they're connected to, what mod is being downloaded and more. My hope is that people will actually see this while it's running to know for sure that they can trust their admin. Hey, not everyone reads this. Also now that serversync is it's own entity the entire console is there for debugging purposes. * "I have files such as optifine that I don't want the server to delete" - * Well then specify that in the config file. It can ignore a download or deletion of any file you want. -* "I want to change how the UI looks so it doesn't say 'The Verse' " - * This mod requires CustomMainMenu, read up on the CustomMainMenu documents for all of that. + * Specify this in the config files IGNORE_LIST + * Add client only mods to the clientmods directory on the server + * Mods in the clientmods folder are automatically added to the ignore list What does it do exactly? ----------- @@ -68,27 +84,35 @@ What does it do exactly? * If the server receives the exists command the server will will return a boolean value of weather or not the file requested exists on the server. * If the server receieves the exit command it will close the connection and destroy the thread. However, this will also happen automatically after X ammount of time as defined in the config file -* When the client clicks the button which has the ID defined in the config file the client will start the update script +* When the client runs serversync the update script starts * The client will first request the name of all the files on the server. * The client will then iterate through each of these files * If this file exists on the client it will take a checksum of it and ask the server if they are the same -* If the files are differnt it will send the update command to pull a new file +* If the files are different it will send the update command to pull a new file * If the client does not have the file it will send the update command to download it * After iterating through all of the server files the client will then iterate through all of it's own files * If the client has a file that the server does not, it will delete it. -* When all this finishes if there were updated files the client will ask the user if she/he wants to restart the client to apply the changes -* If there are no files to update the client will join the server that is defined in the IP field of the config file + +What you should expect to see +-------------- + +When the program starts up you will see a very lightweight console with general information on progress etc. + +![Information Console](http://s31.postimg.org/bxc807u63/ss_snap.png) + +Error messages will appear if the server cannot be found or there is some exception in the mod while downloading updates. All these errors should be fairly self explanatory and easy to fix. + Compiling -------------- -Simply git clone my repo, cd into the folder and run +Simply git clone the repo, cd into the folder and run ``` -./gradlew build +./gradlew shadowJar ``` or for windows ``` -gradlew.bat build +gradlew.bat shadowJar ``` If you would like to setup a workspace to work on these files simply run @@ -102,29 +126,4 @@ Then change the working directory of your eclipse to ./eclipse ``` -Have at it. - -I don't expect these instructionis to change, but there will undoubtedly come a point where everyone has moved on from minecraft, forge makes some major changes that I don't want to keep up with, or I simply lose interest in maintaining this project. - -What you should expect to see --------------- - -When the game starts up you will see a very lightweight menu. - -![Start Screen](/previewImages/startScreen.bmp) - -Upon clicking the connect button, the client will start checking for updates. - -![Downloading](/previewImages/downloading.bmp) - -If there was a download to be had, the mod will notify the user. - -![Relaunch](/previewImages/relaunch.bmp) - -If the user decides to click no and then connect again it will tell the user an update is pending. - -![Click Twice](/previewImages/clickTwice.bmp) - -A similar error message will appear if the server cannot be found or there is some exception in the mod while downloading updates. All these errors are solved simply by restarting minecraft and making sure the server is up. If there is nothing to download it will automatically connect to user to the server. - -![Connected](/previewImages/connected.bmp) +Have at it. \ No newline at end of file diff --git a/Shadow-Licence.txt b/Shadow-Licence.txt new file mode 100644 index 00000000..989e2c59 --- /dev/null +++ b/Shadow-Licence.txt @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/bin/assets/serversync/TheVerse.png b/bin/assets/serversync/TheVerse.png deleted file mode 100755 index 8a6d5c12..00000000 Binary files a/bin/assets/serversync/TheVerse.png and /dev/null differ diff --git a/bin/assets/serversync/versebutton_1.png b/bin/assets/serversync/versebutton_1.png deleted file mode 100755 index d74dc814..00000000 Binary files a/bin/assets/serversync/versebutton_1.png and /dev/null differ diff --git a/bin/assets/serversync/versebutton_2.png b/bin/assets/serversync/versebutton_2.png deleted file mode 100755 index 1cb9a9c0..00000000 Binary files a/bin/assets/serversync/versebutton_2.png and /dev/null differ diff --git a/bin/com/superzanti/lib/RefStrings.class b/bin/com/superzanti/lib/RefStrings.class deleted file mode 100644 index 20077e98..00000000 Binary files a/bin/com/superzanti/lib/RefStrings.class and /dev/null differ diff --git a/bin/com/superzanti/serversync/ClientProxy.class b/bin/com/superzanti/serversync/ClientProxy.class deleted file mode 100644 index 0744c8e0..00000000 Binary files a/bin/com/superzanti/serversync/ClientProxy.class and /dev/null differ diff --git a/bin/com/superzanti/serversync/CommonProxy.class b/bin/com/superzanti/serversync/CommonProxy.class deleted file mode 100644 index 139e682b..00000000 Binary files a/bin/com/superzanti/serversync/CommonProxy.class and /dev/null differ diff --git a/bin/com/superzanti/serversync/GuiScreenHandler.class b/bin/com/superzanti/serversync/GuiScreenHandler.class deleted file mode 100644 index 1ad89b0a..00000000 Binary files a/bin/com/superzanti/serversync/GuiScreenHandler.class and /dev/null differ diff --git a/bin/com/superzanti/serversync/Md5.class b/bin/com/superzanti/serversync/Md5.class deleted file mode 100644 index 39e419ca..00000000 Binary files a/bin/com/superzanti/serversync/Md5.class and /dev/null differ diff --git a/bin/com/superzanti/serversync/ServerSyncRegistry.class b/bin/com/superzanti/serversync/ServerSyncRegistry.class deleted file mode 100644 index 132f0be3..00000000 Binary files a/bin/com/superzanti/serversync/ServerSyncRegistry.class and /dev/null differ diff --git a/bin/com/superzanti/serversync/SyncClient.class b/bin/com/superzanti/serversync/SyncClient.class deleted file mode 100644 index a8da8c2f..00000000 Binary files a/bin/com/superzanti/serversync/SyncClient.class and /dev/null differ diff --git a/bin/com/superzanti/serversync/SyncClientConnection.class b/bin/com/superzanti/serversync/SyncClientConnection.class deleted file mode 100644 index 694ffe10..00000000 Binary files a/bin/com/superzanti/serversync/SyncClientConnection.class and /dev/null differ diff --git a/bin/com/superzanti/serversync/SyncServer.class b/bin/com/superzanti/serversync/SyncServer.class deleted file mode 100644 index b226e1f0..00000000 Binary files a/bin/com/superzanti/serversync/SyncServer.class and /dev/null differ diff --git a/bin/com/superzanti/serversync/SyncServerConnection.class b/bin/com/superzanti/serversync/SyncServerConnection.class deleted file mode 100644 index fb37cd33..00000000 Binary files a/bin/com/superzanti/serversync/SyncServerConnection.class and /dev/null differ diff --git a/bin/mcmod.info b/bin/mcmod.info deleted file mode 100644 index d44ca3cc..00000000 --- a/bin/mcmod.info +++ /dev/null @@ -1,16 +0,0 @@ -[ -{ - "modid": "serversync", - "name": "Server Sync", - "description": "Sync files between client and server for Minecraft Forge", - "version": "${version}", - "mcversion": "${mcversion}", - "url": "", - "updateUrl": "", - "authorList": ["SuperZanti"], - "credits": "", - "logoFile": "", - "screenshots": [], - "dependencies": [] -} -] diff --git a/build.gradle b/build.gradle index 2ae78bbd..e5d6497f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,7 @@ buildscript { repositories { mavenCentral() + jcenter() maven { name = "forge" url = "http://files.minecraftforge.net/maven" @@ -12,12 +13,17 @@ buildscript { } dependencies { classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT' + classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.3' } } apply plugin: 'forge' +apply plugin: 'eclipse' +apply plugin: 'java' +apply plugin: 'com.github.johnrengelman.shadow' -version = "2.4" + +version = "2.5.2" group= "com.superzanti.serversync" // http://maven.apache.org/guides/mini/guide-naming-conventions.html archivesBaseName = "serversync" @@ -26,11 +32,31 @@ minecraft { runDir = "eclipse" } +configurations { + includedLibs +} + +jar { + manifest { + attributes 'Main-Class': 'runme.Main' + } +} + +shadowJar { + dependencies { + include(dependency('com.google.code.gson:gson:2.6.2')) + } + classifier = '' +} + dependencies { // you may put jars on which you depend on in ./libs // or you may define them like so.. //compile "some.group:artifact:version:classifier" //compile "some.group:artifact:version" + runtime 'com.google.code.gson:gson:2.6.2' + includedLibs 'com.google.code.gson:gson:2.6.2' + shadow 'com.google.code.gson:gson:2.6.2' // real examples //compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env diff --git a/build/libs/serversync-2.2.jar b/build/libs/serversync-2.2.jar deleted file mode 100644 index 423da762..00000000 Binary files a/build/libs/serversync-2.2.jar and /dev/null differ diff --git a/build/libs/serversync-2.3.jar b/build/libs/serversync-2.3.jar deleted file mode 100644 index fd9f8186..00000000 Binary files a/build/libs/serversync-2.3.jar and /dev/null differ diff --git a/build/libs/serversync-2.4.jar b/build/libs/serversync-2.4.jar deleted file mode 100644 index 13815b86..00000000 Binary files a/build/libs/serversync-2.4.jar and /dev/null differ diff --git a/eclipse/config/CustomMainMenu/mainmenu.json b/eclipse/config/CustomMainMenu/mainmenu.json deleted file mode 100644 index 0abe08b5..00000000 --- a/eclipse/config/CustomMainMenu/mainmenu.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "images": - { - "title": - { - "image" : "serversync:TheVerse.png", - "posX" : -150, - "posY" : 25, - "width" : 300, - "height" : 55, - "alignment" : "top_center" - } - }, - - "buttons": - { - "singleplayer": - { - "text" : "menu.singleplayer", - "texture" : "serversync:versebutton_1.png", - "posX" : -100, - "posY" : 48, - "width" : 200, - "height" : 20, - "action" : - { - "type" : "openGui", - "gui" : "singleplayer" - } - }, - - "connectToServer": - { - "text" : "Connect To The Verse", - "texture" : "serversync:versebutton_1.png", - "posX" : -100, - "posY" : 72, - "width" : 200, - "height" : 20, - "action" : - { - "type" : "openGui", - "gui" : "connectToServer" - } - }, - - "options": - { - "text" : "menu.options", - "texture" : "serversync:versebutton_2.png", - "posX" : -100, - "posY" : 112, - "width" : 98, - "height" : 20, - "action" : - { - "type" : "openGui", - "gui" : "options" - } - }, - - "quit": - { - "text" : "menu.quit", - "texture" : "serversync:versebutton_2.png", - "posX" : 2, - "posY" : 112, - "width" : 98, - "height" : 20, - "action" : - { - "type" : "quit" - } - } - }, - - "texts": - { - "disclaimer": - { - "text" : "Warning: ServerSync allows a server to put any file on your computer", - "posX": 5, - "posY": -10, - "color" : 16777215, - "alignment" : "bottom_left" - } - }, - - "other": - { - "splash-text": - { - "posX" : 90, - "posY" : 70, - "color" : -256, - "alignment" : "top_center", - "file" : "texts/splashes.txt" - }, - - "panorama": - { - "images" : "minecraft:textures/gui/title/background/panorama_%c.png", - "animate" : true, - "animationSpeed" : 1, - "blur" : true, - "gradient" : true - } - } -} \ No newline at end of file diff --git a/eclipse/config/forge.cfg b/eclipse/config/forge.cfg deleted file mode 100644 index 976d1345..00000000 --- a/eclipse/config/forge.cfg +++ /dev/null @@ -1,57 +0,0 @@ -# Configuration file - -general { - # Set to true to disable Forge's version check mechanics. Forge queries a small json file on our server for version information. For more details see the ForgeVersion class in our github. - B:disableVersionCheck=false - - # Controls the number threshold at which Packet51 is preferred over Packet52, default and minimum 64, maximum 1024 - I:clumpingThreshold=64 - - # Set to true to enable the post initialization sorting of crafting recipes using Forge's sorter. May cause desyncing on conflicting recipies. MUST RESTART MINECRAFT IF CHANGED FROM THE CONFIG GUI. - B:sortRecipies=true - - # Set this to true to remove any Entity that throws an error in its update method instead of closing the server and reporting a crash log. BE WARNED THIS COULD SCREW UP EVERYTHING USE SPARINGLY WE ARE NOT RESPONSIBLE FOR DAMAGES. - B:removeErroringEntities=false - - # Set this to true to remove any TileEntity that throws an error in its update method instead of closing the server and reporting a crash log. BE WARNED THIS COULD SCREW UP EVERYTHING USE SPARINGLY WE ARE NOT RESPONSIBLE FOR DAMAGES. - B:removeErroringTileEntities=false - - # Set this to true to check the entire entity's collision bounding box for ladders instead of just the block they are in. Causes noticable differences in mechanics so default is vanilla behavior. Default: false - B:fullBoundingBoxLadders=false - - # Control the range of sky blending for colored skies in biomes. - I:biomeSkyBlendRange < - 2 - 4 - 6 - 8 - 10 - 12 - 14 - 16 - 18 - 20 - 22 - 24 - 26 - 28 - 30 - 32 - 34 - > - - # Base zombie summoning spawn chance. Allows changing the bonus zombie summoning mechanic. - D:zombieBaseSummonChance=0.1 - - # Chance that a zombie (or subclass) is a baby. Allows changing the zombie spawning mechanic. - D:zombieBabyChance=0.05 - - # The spawn fuzz when a player respawns in the world, this is controlable by WorldType, this config option is for the default overworld. - I:defaultSpawnFuzz=20 - - # If the overworld has ANY spawn fuzz at all. If not, the spawn will always be the exact same location. - B:spawnHasFuzz=true - B:enableGlobalConfig=false -} - - diff --git a/eclipse/config/forgeChunkLoading.cfg b/eclipse/config/forgeChunkLoading.cfg deleted file mode 100644 index 3051bdff..00000000 --- a/eclipse/config/forgeChunkLoading.cfg +++ /dev/null @@ -1,47 +0,0 @@ -# Configuration file - -########################################################################################################## -# Forge -#--------------------------------------------------------------------------------------------------------# -# Sample mod specific control section. -# Copy this section and rename the with the modid for the mod you wish to override. -# A value of zero in either entry effectively disables any chunkloading capabilities -# for that mod -########################################################################################################## - -Forge { - # Maximum chunks per ticket for the mod. - I:maximumChunksPerTicket=25 - - # Maximum ticket count for the mod. Zero disables chunkloading capabilities. - I:maximumTicketCount=200 -} - - -########################################################################################################## -# defaults -#--------------------------------------------------------------------------------------------------------# -# Default configuration for forge chunk loading control -########################################################################################################## - -defaults { - # Are mod overrides enabled? - B:enabled=true - - # The default maximum number of chunks a mod can force, per ticket, - # for a mod without an override. This is the maximum number of chunks a single ticket can force. - I:maximumChunksPerTicket=25 - - # The default maximum ticket count for a mod which does not have an override - # in this file. This is the number of chunk loading requests a mod is allowed to make. - I:maximumTicketCount=200 - - # The number of tickets a player can be assigned instead of a mod. This is shared across all mods and it is up to the mods to use it. - I:playerTicketCount=500 - - # Unloaded chunks can first be kept in a dormant cache for quicker - # loading times. Specify the size (in chunks) of that cache here - I:dormantChunkCacheSize=0 -} - - diff --git a/eclipse/config/serversync.cfg b/eclipse/config/serversync.cfg deleted file mode 100644 index b3b03987..00000000 --- a/eclipse/config/serversync.cfg +++ /dev/null @@ -1,65 +0,0 @@ -# Configuration file - -gui { - # The ID of the button that connects to the server and updates [range: 0 ~ 2147483647, default: 6001] - I:ButtonID=6001 -} - - -ignoredfiles { - # These files are ignored by serversync. DO NOT IGNORE serversync.cfg - S:IGNORE_LIST < - ./mods/CustomMainMenu-MC1.7.10-1.5.jar - ./config/CustomMainMenu/mainmenu.json - ./config/serversync.cfg - ./config/forge.cfg - ./config/forgeChunkLoading.cfg - ./config/splash.properties - ./mods/serversync-1.2.jar - ./mods/CustomMainMenu-C1.7.10-1.5-debof.jar - > -} - - -serverconnection { - # The port in which the minecraft server is running, not the serversync port [range: 1 ~ 49151, default: 25565] - I:MINECRAFT_PORT=25565 - - # The IP address of the server [default: 127.0.0.1] - S:SERVER_IP=minecraft.sethmiers.com - - # The port that your server will be serving on [range: 1 ~ 49151, default: 38067] - I:SERVER_PORT=36730 -} - - -serverencryption { - # The check command security key phrase [default: 0ba4439ee9a46d9d9f14c60f88f45f87] - S:SECURE_CHECK=0ba4439ee9a46d9d9f14c60f88f45f87 - - # The check-mods command security key phrase [default: 3dd3152ae3e427aa2817df12570ea708] - S:SECURE_CHECKMODS=3dd3152ae3e427aa2817df12570ea708 - - # The checksum command security key phrase [default: 226190d94b21d1b0c7b1a42d855e419d] - S:SECURE_CHECKSUM=226190d94b21d1b0c7b1a42d855e419d - - # The exists command security key phrase [default: e087923eb5dd1310f5f25ddd5ae5b580] - S:SECURE_EXISTS=e087923eb5dd1310f5f25ddd5ae5b580 - - # The exit command security key phrase [default: f24f62eeb789199b9b2e467df3b1876b] - S:SECURE_EXIT=f24f62eeb789199b9b2e467df3b1876b - - # The recursive command security key phrase [default: f8e45531a3ea3d5c1247b004985175a4] - S:SECURE_RECURSIVE=f8e45531a3ea3d5c1247b004985175a4 - - # The update command security key phrase [default: 3ac340832f29c11538fbe2d6f75e8bcc] - S:SECURE_UPDATE=3ac340832f29c11538fbe2d6f75e8bcc -} - - -storagevariables { - # DO NOT EDIT THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING! (If you are a server feel free to change it as much as you want to update your clients) [default: 20150608_000500] - S:LAST_UPDATE=20150608_000500 -} - - diff --git a/eclipse/config/splash.properties b/eclipse/config/splash.properties deleted file mode 100644 index a5f02746..00000000 --- a/eclipse/config/splash.properties +++ /dev/null @@ -1,14 +0,0 @@ -#Splash screen properties -#Sat Jun 20 21:11:18 UTC 2015 -logoTexture=textures/gui/title/mojang.png -background=0xFFFFFF -font=0x0 -barBackground=0xFFFFFF -barBorder=0xC0C0C0 -rotate=false -bar=0xCB3D35 -enabled=true -resourcePackPath=resources -logoOffset=0 -forgeTexture=fml\:textures/gui/forge.gif -fontTexture=textures/font/ascii.png diff --git a/eclipse/mods/CustomMainMenu-C1.7.10-1.5-debof.jar b/eclipse/mods/CustomMainMenu-C1.7.10-1.5-debof.jar deleted file mode 100644 index 0600a4f9..00000000 Binary files a/eclipse/mods/CustomMainMenu-C1.7.10-1.5-debof.jar and /dev/null differ diff --git a/src/main/java/com/superzanti/lib/RefStrings.java b/src/main/java/com/superzanti/lib/RefStrings.java index 7d3801ea..d4702203 100755 --- a/src/main/java/com/superzanti/lib/RefStrings.java +++ b/src/main/java/com/superzanti/lib/RefStrings.java @@ -3,5 +3,5 @@ public class RefStrings { public static final String MODID = "serversync"; public static final String NAME = "Server Sync"; - public static final String VERSION = "2.4"; + public static final String VERSION = "2.5.2"; } \ No newline at end of file diff --git a/src/main/java/com/superzanti/serversync/ClientProxy.java b/src/main/java/com/superzanti/serversync/ClientProxy.java deleted file mode 100755 index 5eac1972..00000000 --- a/src/main/java/com/superzanti/serversync/ClientProxy.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.superzanti.serversync; - -import com.superzanti.serversync.CommonProxy; - -public class ClientProxy extends CommonProxy { - - protected static com.superzanti.serversync.SyncClient syncclient; - - @Override - public boolean isClient() { - return true; - } - - @Override - public boolean isServer() { - return false; - } - -} \ No newline at end of file diff --git a/src/main/java/com/superzanti/serversync/ClientWorker.java b/src/main/java/com/superzanti/serversync/ClientWorker.java new file mode 100644 index 00000000..ec0a1bab --- /dev/null +++ b/src/main/java/com/superzanti/serversync/ClientWorker.java @@ -0,0 +1,227 @@ +package com.superzanti.serversync; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import com.superzanti.serversync.util.Logger; +import com.superzanti.serversync.util.PathUtils; +import com.superzanti.serversync.util.Server; +import com.superzanti.serversync.util.SyncFile; + +import runme.Main; + +/** + * Deals with all of the synchronizing for the client, this works without + * starting minecraft + * + * @author Rheimus + * + */ +public class ClientWorker implements Runnable { + + private boolean errorInUpdates; + private boolean updateHappened; + private boolean finished; + private Logger logs; + + private List clientFiles; + + public ClientWorker() { + logs = new Logger(); + clientFiles = new ArrayList(); + errorInUpdates = false; + updateHappened = false; + finished = false; + + final class Shutdown extends Thread { + + @Override + public void run() { + logs.save(); + } + + } + // Save full log on shutdown + Runtime.getRuntime().addShutdownHook(new Shutdown()); + + } + + public Logger getLogger() { + return logs; + } + + public boolean getErrors() { + return errorInUpdates; + } + + public boolean getUpdates() { + return updateHappened; + } + + public boolean isFinished() { + return finished; + } + + private void closeWorker(Server server) { + server.close(); + if (!finished) { + try { + if (!updateHappened && !errorInUpdates) { + Main.updateText( + "No update needed, for a full log check the logs folder in the minecraft directory"); + Main.updateProgress(100); + } else { + logs.updateLogs("Files updated", Logger.FULL_LOG); + Main.updateProgress(100); + } + if (errorInUpdates) { + logs.updateLogs( + "Errors occured, please check ip/port details are correct. For a detailed log check the logs folder in your minecraft directory"); + } + Thread.sleep(100); + Main.toggleButton(); + } catch (InterruptedException e) { + logs.updateLogs("Exception caught! - " + e, Logger.FULL_LOG); + Main.toggleButton(); + } + finished = true; + } else { + Main.toggleButton(); + } + } + + @Override + public void run() { + Server server = new Server(this, SyncConfig.SERVER_IP, SyncConfig.SERVER_PORT); + boolean updateNeeded = false; + updateHappened = false; + + try { + // Get clients mod list + List cMods = PathUtils.fileListDeep(Paths.get("../mods/")); + List cConfigs = PathUtils.fileListDeep(Paths.get("../config/")); + if (cMods != null) { + for (Path path : cMods) { + SyncFile f = new SyncFile(path); + clientFiles.add(f); + } + } + + if (!server.connect()) { + errorInUpdates = true; + finished = true; + closeWorker(server); + return; + } + + logs.updateLogs("Updating serversync config..."); + server.getConfig(); + if (cConfigs != null) { + for (Path path : cConfigs) { + String fileName = path.getFileName().toString(); + if (SyncConfig.INCLUDE_LIST.contains(fileName)) { + clientFiles.add(new SyncFile(path, false)); + } + } + } + updateNeeded = server.isUpdateNeeded(clientFiles); + + if (updateNeeded) { + updateHappened = true; + logs.updateLogs("The mods between server and client are incompatable... Updating..."); + + // get all files on server + logs.updateLogs("Getting mods..."); + ArrayList serverFiles = server.getFiles(); + + logs.updateLogs("Ignoring: " + SyncConfig.IGNORE_LIST, Logger.FULL_LOG); + // run calculations to figure out how big the bar is + float numberOfFiles = clientFiles.size() + serverFiles.size(); + float percentScale = numberOfFiles / 100; + float currentPercent = 0; + + /* UPDATING */ + logs.updateLogs("<------> Starting Update Process <------>"); + // Client only mods are added on the server side and treated as + // normal mods by the client + for (SyncFile file : serverFiles) { + // Update status + currentPercent++; + + Path clientPath = file.CLIENT_MODPATH; + // Get file at rPath location + boolean exists = Files.exists(clientPath); + + // Exists + if (exists) { + SyncFile clientFile = new SyncFile(clientPath); + if (!clientFile.compare(file)) { + server.updateFile(file.MODPATH.toString(), clientPath.toFile()); + } else { + logs.updateLogs(file.fileName + " is up to date"); + } + } else { + // only need to check for ignore here as we are working + // on the servers file tree + if (file.isSetToIgnore() && !file.clientOnlyMod) { + logs.updateLogs("<>" + file.fileName + " set to ignore"); + } else { + logs.updateLogs(file.fileName + " Does not exist...", Logger.FULL_LOG); + server.updateFile(file.MODPATH.toString(), clientPath.toFile()); + } + } + Main.updateProgress((int) (currentPercent / percentScale)); + } + + /* DELETION */ + logs.updateLogs("<------> Starting Deletion Process <------>"); + // Parse clients file tree + for (SyncFile file : clientFiles) { + currentPercent++; + + // check for files that need to be deleted + if (file.isSetToIgnore()) { + logs.updateLogs(file.fileName + " set to ignore", Logger.FULL_LOG); + } else { + // Not present in server list + logs.updateLogs("Checking client's " + file.fileName + " against server", Logger.FULL_LOG); + boolean exists = server.modExists(file); + if (!exists) { + logs.updateLogs(file.fileName + " Does not match... attempting phase 1 delete", + Logger.FULL_LOG); + + // File fails to delete + try { + file.delete(); + logs.updateLogs("<>" + file.fileName + " deleted"); + } catch(IOException e) { + logs.updateLogs(file.fileName + "Failed to delete flagging for deleteOnExit", + Logger.FULL_LOG); + file.deleteOnExit(); + } + + updateHappened = true; + } + Main.updateProgress((int) (currentPercent / percentScale)); + } + } + } + + server.exit(); + + logs.updateLogs("Update Complete! Have a nice day!"); + } catch (Exception e) { + logs.updateLogs("Exception caught! - " + e, Logger.FULL_LOG); + e.printStackTrace(); + errorInUpdates = true; + } finally { + closeWorker(server); + } + + } + +} diff --git a/src/main/java/com/superzanti/serversync/GuiScreenHandler.java b/src/main/java/com/superzanti/serversync/GuiScreenHandler.java deleted file mode 100755 index 82f86b0b..00000000 --- a/src/main/java/com/superzanti/serversync/GuiScreenHandler.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.superzanti.serversync; - -import java.nio.file.Paths; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiErrorScreen; -import net.minecraftforge.client.event.GuiScreenEvent.ActionPerformedEvent; -import net.minecraftforge.common.MinecraftForge; -import cpw.mods.fml.common.eventhandler.SubscribeEvent; -import cpw.mods.fml.relauncher.SideOnly; -import cpw.mods.fml.relauncher.Side; - -@SideOnly(Side.CLIENT) -public class GuiScreenHandler{ - - public static boolean doesButtonWork = true; - - @SubscribeEvent - public void onActionPerformedPre (ActionPerformedEvent.Post event) { - - if(event.button.id > 0) - ServerSyncRegistry.logger.info("Your BUTTON_ID is: " + event.button.id); - - if (event.button.id == ServerSyncRegistry.BUTTON_ID) { - // log our absolute path so the user knows where to delete things - ServerSyncRegistry.logger.info("Files may be downloaded to this location:"); - ServerSyncRegistry.logger.info(Paths.get(".").toAbsolutePath()); - - if(!doesButtonWork){ - GuiErrorScreen guierrorscreen = new GuiErrorScreen("A Minecraft restart is pending...", "Please restart your client to play multiplayer."); - Minecraft.getMinecraft().displayGuiScreen(guierrorscreen); - } else { - MinecraftForge.EVENT_BUS.register(ClientProxy.syncclient); - try{ - client(); - } catch (Exception e) { - ServerSyncRegistry.logger.error("Exception caught! - " + e); - e.printStackTrace(); - } - } - } - return; - } - - private static void client() throws Exception { - ClientProxy.syncclient.runClient(); - return; - } - -} diff --git a/src/main/java/com/superzanti/serversync/ServerSetup.java b/src/main/java/com/superzanti/serversync/ServerSetup.java new file mode 100644 index 00000000..9695efc9 --- /dev/null +++ b/src/main/java/com/superzanti/serversync/ServerSetup.java @@ -0,0 +1,102 @@ +package com.superzanti.serversync; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; + +import com.superzanti.serversync.util.PathUtils; +import com.superzanti.serversync.util.SyncFile; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +/** + * Sets up listener for clients on the server + * + * @author superzanti + */ +@SideOnly(Side.SERVER) +public class ServerSetup implements Runnable { + + // static ServerSocket variable + private static ServerSocket server; + + // This is what's in our folders + private static ArrayList allMods = new ArrayList(); + + protected ServerSetup() { + ArrayList tempList = null; + ArrayList directories = new ArrayList(); + /* DEFAULT DIRECTORIES */ + directories.add("mods"); + + if (SyncConfig.PUSH_CLIENT_MODS) { + directories.add("clientmods"); + } + + if (Files.exists(Paths.get("flan"))) { + directories.add("flan"); + ServerSync.logger.info("Found flans mod, adding content packs to modlist"); + } + + try { + for (String directory : directories) { + String dirString = Paths.get(directory).toAbsolutePath().normalize().toString(); + tempList = PathUtils.fileListDeep(Paths.get(directory)); + ServerSync.logger.info("Getting all of: " + dirString); + if (tempList != null) { + allMods.addAll(SyncFile.parseList(tempList)); + } else { + ServerSync.logger.error("Could not access: " + dirString); + } + ServerSync.logger.info("Finished getting: " + dirString); + } + + /* CONFIGS */ + if (!SyncConfig.INCLUDE_LIST.isEmpty()) { + tempList = PathUtils.fileListDeep(Paths.get("config")); + if (tempList != null) { + for (Path path : tempList) { + if (SyncConfig.INCLUDE_LIST.contains(path.getFileName().toString().replaceAll(" ", ""))) { + allMods.add(new SyncFile(path,false)); + } + } + } + } + + } catch (IOException e) { + //TODO handle exceptions when loading server + ServerSync.logger.error(e.getMessage()); + } + } + + @Override + public void run() { + // create the socket server object + ServerSync.logger.info("Creating new server socket"); + try { + server = new ServerSocket(SyncConfig.SERVER_PORT); + } catch (IOException e) { + ServerSync.logger.info("Error occured." + e); + e.printStackTrace(); + } + // keep listens indefinitely until receives 'exit' call or program + // terminates + ServerSync.logger.info("Now accepting clients..."); + while (true) { + try { + Socket socket = server.accept(); + ServerWorker sc; + sc = new ServerWorker(socket, allMods, server); + new Thread(sc).start(); + } catch (Exception e) { + ServerSync.logger.info("Error occured." + e); + e.printStackTrace(); + } + } + } +} diff --git a/src/main/java/com/superzanti/serversync/ServerSync.java b/src/main/java/com/superzanti/serversync/ServerSync.java new file mode 100644 index 00000000..165f4c3b --- /dev/null +++ b/src/main/java/com/superzanti/serversync/ServerSync.java @@ -0,0 +1,65 @@ +package com.superzanti.serversync; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.apache.logging.log4j.Logger; + +import com.superzanti.lib.RefStrings; +import com.superzanti.serversync.proxy.ClientProxy; +import com.superzanti.serversync.proxy.CommonProxy; + +import cpw.mods.fml.common.Mod; +import cpw.mods.fml.common.Mod.EventHandler; +import cpw.mods.fml.common.SidedProxy; +import cpw.mods.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.common.MinecraftForge; + +/** + * Main class, used to initialize config/logger, register proxies/events and register the mod for FML loading + * @author superzanti + */ +@Mod(modid = RefStrings.MODID, name = RefStrings.NAME, version = RefStrings.VERSION) +public class ServerSync { + public static Logger logger; + + @SidedProxy(modId = RefStrings.MODID, clientSide = "com.superzanti.serversync.proxy.ClientProxy", serverSide = "com.superzanti.serversync.proxy.CommonProxy") + private static CommonProxy proxy; + + @EventHandler + public static void PreLoad(FMLPreInitializationEvent PreEvent) { + // setup the minecraft logger for the server + logger = PreEvent.getModLog(); + + // Create clientmods directory + if (proxy.isServer()) { + Path clientOnlyMods = Paths.get("clientmods/"); + if (!Files.exists(clientOnlyMods)) { + try { + Files.createDirectories(clientOnlyMods); + } catch (IOException e) { + logger.error("Could not create clientmods directory"); + } + } + } + + + // Client side + if (proxy.isClient()) { + logger.info("I am a client"); + MinecraftForge.EVENT_BUS.register(new ClientProxy()); + } + + // Server side + if (proxy.isServer()) { + logger.info("I am a server"); + // Grab the configuration file and load in the values + SyncConfig.init(PreEvent); + ServerSetup setup = new ServerSetup(); + Thread syncthread = new Thread(setup); + syncthread.start(); + } + } +} diff --git a/src/main/java/com/superzanti/serversync/ServerSyncRegistry.java b/src/main/java/com/superzanti/serversync/ServerSyncRegistry.java deleted file mode 100755 index 21902888..00000000 --- a/src/main/java/com/superzanti/serversync/ServerSyncRegistry.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.superzanti.serversync; - -import java.util.Arrays; -import java.util.List; - -import org.apache.logging.log4j.Logger; - -import com.superzanti.lib.RefStrings; - -import cpw.mods.fml.common.Loader; -import cpw.mods.fml.common.Mod; -import cpw.mods.fml.common.Mod.EventHandler; -import cpw.mods.fml.common.SidedProxy; -import cpw.mods.fml.common.event.FMLPreInitializationEvent; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.common.config.Configuration; -import net.minecraftforge.common.config.Property; - -@Mod(modid = RefStrings.MODID, name = RefStrings.NAME, version = RefStrings.VERSION) -public class ServerSyncRegistry { - protected static Logger logger; - protected static Configuration config; - protected static String SERVER_IP; - protected static int SERVER_PORT; - protected static int MINECRAFT_PORT; - protected static String SECURE_CHECK; - protected static String SECURE_CHECKMODS; - protected static String SECURE_RECURSIVE; - protected static String SECURE_CHECKSUM; - protected static String SECURE_UPDATE; - protected static String SECURE_EXISTS; - protected static String SECURE_EXIT; - protected static List IGNORE_LIST; - protected static int BUTTON_ID; - protected static String LAST_UPDATE; - - protected static final String CLIENT_PROXY = "com.superzanti.serversync.ClientProxy"; - protected static final String SERVER_PROXY = "com.superzanti.serversync.CommonProxy"; - @SidedProxy(modId = RefStrings.MODID, clientSide = CLIENT_PROXY, serverSide = SERVER_PROXY) - protected static CommonProxy proxy; - - @EventHandler - public static void PreLoad(FMLPreInitializationEvent PreEvent) { - // MinecraftForge.EVENT_BUS.register(new SyncClient()); - - // setup the logger for the mod - logger = PreEvent.getModLog(); - - // Grab the configuration file and load in the values - config = new Configuration(PreEvent.getSuggestedConfigurationFile()); - - config.load(); - - SERVER_IP = config.getString("SERVER_IP", "ServerConnection", "127.0.0.1", "The IP address of the server"); - SERVER_PORT = config.getInt("SERVER_PORT", "ServerConnection", 38067, 1, 49151, - "The port that your server will be serving on"); - MINECRAFT_PORT = config.getInt("MINECRAFT_PORT", "ServerConnection", 25565, 1, 49151, - "The port in which the minecraft server is running, not the serversync port"); - - SECURE_CHECK = config.getString("SECURE_CHECK", "ServerEncryption", "0ba4439ee9a46d9d9f14c60f88f45f87", - "The check command security key phrase"); - SECURE_CHECKMODS = config.getString("SECURE_CHECKMODS", "ServerEncryption", "3dd3152ae3e427aa2817df12570ea708", - "The check-mods command security key phrase"); - SECURE_RECURSIVE = config.getString("SECURE_RECURSIVE", "ServerEncryption", "f8e45531a3ea3d5c1247b004985175a4", - "The recursive command security key phrase"); - SECURE_CHECKSUM = config.getString("SECURE_CHECKSUM", "ServerEncryption", "226190d94b21d1b0c7b1a42d855e419d", - "The checksum command security key phrase"); - SECURE_UPDATE = config.getString("SECURE_UPDATE", "ServerEncryption", "3ac340832f29c11538fbe2d6f75e8bcc", - "The update command security key phrase"); - SECURE_EXISTS = config.getString("SECURE_EXISTS", "ServerEncryption", "e087923eb5dd1310f5f25ddd5ae5b580", - "The exists command security key phrase"); - SECURE_EXIT = config.getString("SECURE_EXIT", "ServerEncryption", "f24f62eeb789199b9b2e467df3b1876b", - "The exit command security key phrase"); - - String[] defaultList = { "./mods/CustomMainMenu-MC1.7.10-1.5.jar", "./config/CustomMainMenu/mainmenu.json", - "./config/forge.cfg", "./config/forgeChunkLoading.cfg", "./config/splash.properties" }; - // String[] ignoreList = config.getStringList("IGNORE_LIST", - // "IgnoredFiles", defaultList, "These files are ignored by - // serversync"); - - Property ignoreList = config.get("IgnoredFiles", "IGNORE_LIST", defaultList, - "These files are ignored by serversync. DO NOT IGNORE serversync.cfg"); - IGNORE_LIST = Arrays.asList(ignoreList.getStringList()); - - BUTTON_ID = config.getInt("ButtonID", "GUI", 6001, 0, 2147483647, - "The ID of the button that connects to the server and updates"); - - LAST_UPDATE = config.getString("LAST_UPDATE", "StorageVariables", "20150608_000500", - "DO NOT EDIT THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING! (If you are a server feel free to change it as much as you want to update your clients)"); - - // loading the configuration from its file - config.save(); - - // Client side - if (proxy.isClient()) { - logger.info("I am a client"); - ClientProxy.syncclient = new SyncClient(); - MinecraftForge.EVENT_BUS.register(ClientProxy.syncclient); - MinecraftForge.EVENT_BUS.register(new GuiScreenHandler()); - } - - // Server side - if (proxy.isServer()) { - logger.info("I am a server"); - SyncServer syncserver = new SyncServer(); - Thread syncthread = new Thread(syncserver); - syncthread.start(); - } - return; - } -} diff --git a/src/main/java/com/superzanti/serversync/ServerWorker.java b/src/main/java/com/superzanti/serversync/ServerWorker.java new file mode 100644 index 00000000..f2985ff5 --- /dev/null +++ b/src/main/java/com/superzanti/serversync/ServerWorker.java @@ -0,0 +1,183 @@ +package com.superzanti.serversync; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; + +import com.superzanti.serversync.util.Md5; +import com.superzanti.serversync.util.SyncFile; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import runme.Main; + +/** + * This worker handles requests from the client continuously until told to exit using SECURE_EXIT + * @author superzanti + */ +@SideOnly(Side.SERVER) +public class ServerWorker implements Runnable { + + private static Socket clientsocket; + private static ObjectInputStream ois; + private static ObjectOutputStream oos; + // Contains all mods on the server, including client-side mods etc + private static ArrayList allList = new ArrayList(); + // Contians mods located in the servers clientmods directory + private static ArrayList clientOnlyList = new ArrayList(); + + protected ServerWorker(Socket socket, ArrayList allFiles, ServerSocket theServer){ + clientsocket = socket; + allList = allFiles; + ServerSync.logger.info("Connection established with " + clientsocket); + return; + } + + @Override + public void run() { + try + { + ois = new ObjectInputStream(clientsocket.getInputStream()); + oos = new ObjectOutputStream(clientsocket.getOutputStream()); + oos.flush(); + while(true) { + + String message = (String) ois.readObject(); + ServerSync.logger.info("Received message: "+message+" from connection "+clientsocket); + + if(message.equals(SyncConfig.SECURE_CHECK)) { + oos.writeObject(SyncConfig.LAST_UPDATE); + oos.flush(); + } + + if(message.equals(SyncConfig.SECURE_CHECKMODS)) { + @SuppressWarnings("unchecked") + ArrayList serverModList = SyncFile.listModNames(allList); + ServerSync.logger.info("Syncable mods are: " + serverModList.toString()); + oos.writeObject(serverModList); + oos.flush(); + } + + if(message.equals(SyncConfig.SECURE_RECURSIVE)) { + /*ArrayList ml = new ArrayList(); + for (Mod mod : allList) { + ml.add(mod.MODPATH.toString()); + }*/ + oos.writeObject(allList); + oos.flush(); + } + + if(message.equals(SyncConfig.SECURE_CHECKSUM)) { + File theFile = (File) ois.readObject(); + String serverChecksum = Md5.md5String(theFile); + oos.writeObject(serverChecksum); + oos.flush(); + } + + /*// Not currently in use as clientmods are included in allList + if(message.equals(Main.SECURE_PUSH_CLIENTMODS)) { + oos.writeObject(clientOnlyList); + oos.flush(); + } + */ + + if(message.equals(SyncConfig.SECURE_UPDATE)) { + ServerSync.logger.info("Writing file to client..."); + String theFile = (String) ois.readObject(); + File f = new File(theFile); + byte[] buff = new byte[clientsocket.getSendBufferSize()]; + int bytesRead = 0; + InputStream in = new FileInputStream(f); + while((bytesRead = in.read(buff))>0) { + oos.write(buff,0,bytesRead); + } + in.close(); + oos.flush(); + break; + } + + if(message.equals(SyncConfig.GET_CONFIG)) { + ServerSync.logger.info("Sending config to client..."); + File f = new File("./config/serversync.cfg"); + byte[] buff = new byte[clientsocket.getSendBufferSize()]; + int bytesRead = 0; + InputStream in = new FileInputStream(f); + while((bytesRead = in.read(buff))>0) { + oos.write(buff,0,bytesRead); + } + in.close(); + oos.flush(); + break; + } + + if(message.equals(Main.SECURE_FILESIZE)) { + //TODO update to NIO + ServerSync.logger.info("Writing filesize to client..."); + String theFile = (String) ois.readObject(); + Path p = Paths.get(theFile); + oos.writeLong(Files.size(p)); + oos.flush(); + } + + if(message.equals(SyncConfig.SECURE_EXISTS)) { + String theMod = (String) ois.readObject(); + boolean exists = false; + for(SyncFile m : allList) { + if (m.fileName.equals(theMod)) { + exists = true; + break; + } + } + if (!exists) { + for(SyncFile m : clientOnlyList) { + if (m.fileName.equals(theMod)) { + exists = true; + break; + } + } + } + if(exists) { + oos.writeBoolean(true); + oos.flush(); + } + else { + oos.writeBoolean(false); + oos.flush(); + } + } + + if(message.equals(SyncConfig.SECURE_EXIT)) { + break; + } + } + ServerSync.logger.info("Connection "+clientsocket+" is closing."); + } + catch(Exception e) + { + ServerSync.logger.info("Error occured: "+e); + e.printStackTrace(); + } finally { + try { + if(oos != null) + oos.close(); + if(ois != null) + ois.close(); + if(clientsocket !=null) + clientsocket.close(); + } catch (IOException e) { + ServerSync.logger.info("Error occured: "+e); + e.printStackTrace(); + } + } + return; + } +} diff --git a/src/main/java/com/superzanti/serversync/SyncClient.java b/src/main/java/com/superzanti/serversync/SyncClient.java deleted file mode 100755 index 73460b6b..00000000 --- a/src/main/java/com/superzanti/serversync/SyncClient.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.superzanti.serversync; - -import java.io.File; -import java.nio.file.Path; -import java.nio.file.Paths; - -import com.superzanti.lib.RefStrings; - -import cpw.mods.fml.client.FMLClientHandler; -import cpw.mods.fml.common.FMLCommonHandler; -import cpw.mods.fml.common.eventhandler.SubscribeEvent; -import cpw.mods.fml.relauncher.Side; -import cpw.mods.fml.relauncher.SideOnly; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiErrorScreen; -import net.minecraft.client.gui.GuiMainMenu; -import net.minecraft.client.gui.GuiScreenWorking; -import net.minecraft.client.gui.GuiYesNo; -import net.minecraft.client.gui.GuiYesNoCallback; -import net.minecraftforge.client.event.GuiScreenEvent.DrawScreenEvent; -import net.minecraftforge.common.MinecraftForge; - -@SideOnly(Side.CLIENT) -public class SyncClient implements GuiYesNoCallback{ - - protected static SyncClientConnection syncclientconnecction = new SyncClientConnection(); - protected static Thread thread = new Thread(syncclientconnecction); - - private static GuiMainMenu guimainmenu; - private static GuiYesNo guiyesno; - private static GuiErrorScreen guierrorscreen; - private static GuiScreenWorking guiscreenworking; - - public static Path absoluteModsDirectory = null; - - - protected SyncClient() { - ServerSyncRegistry.logger.info("Client Selected! Read for client routine..."); - } - - protected void runClient() throws Exception { - - guiscreenworking = new GuiScreenWorking(); - Minecraft.getMinecraft().displayGuiScreen(guiscreenworking); - updateScreenWorking(0,"Checking for server updates..."); - - thread.start(); - - } - - protected static void updateScreenWorking(int newPercent, String statusMessage){ - guiscreenworking.displayProgressMessage(statusMessage); - guiscreenworking.setLoadingProgress(newPercent); - guiscreenworking.updateScreen(); - } - - @SubscribeEvent - public void onEachTick(DrawScreenEvent.Pre event){ - - if(SyncClientConnection.getFinished()){ - if(SyncClientConnection.getErrors()){ - guierrorscreen = new GuiErrorScreen("There was an error while connecting", "Is your config file is the same as the server's? Is the server on?"); - Minecraft.getMinecraft().displayGuiScreen(guierrorscreen); - GuiScreenHandler.doesButtonWork = false; - } else if (SyncClientConnection.getUpdates()){ - - guiyesno = new GuiYesNo((GuiYesNoCallback) this, "You will need to re-launch minecraft to apply the changes.", "Would you like to do this now?", 0); - Minecraft.getMinecraft().displayGuiScreen(guiyesno); - GuiScreenHandler.doesButtonWork = false; - } else { - FMLClientHandler.instance().connectToServerAtStartup(ServerSyncRegistry.SERVER_IP, ServerSyncRegistry.MINECRAFT_PORT); - GuiScreenHandler.doesButtonWork = true; - } - MinecraftForge.EVENT_BUS.unregister(ClientProxy.syncclient); - } - - } - - @Override - public void confirmClicked(boolean yesButton, int whatsThisInt) { - if(yesButton) { - final class Shutdown extends Thread { - String modsDir = Paths.get("mods/").toAbsolutePath().toString(); - - @Override - public void run() { - try { - //new File(modsDir + "/imHere").createNewFile();// Creates in base mods foler - // Not running ? needs more time perhaps - Runtime.getRuntime().exec("java -cp "+RefStrings.MODID+"-"+RefStrings.VERSION+".jar runme.Main", null, new File(modsDir)); - } catch (Exception e) { - e.printStackTrace(); - } - } - - } - Runtime.getRuntime().addShutdownHook(new Shutdown()); - FMLCommonHandler.instance().exitJava(0, false); - } else { - Minecraft.getMinecraft().displayGuiScreen(guimainmenu); - } - } - -} diff --git a/src/main/java/com/superzanti/serversync/SyncClientConnection.java b/src/main/java/com/superzanti/serversync/SyncClientConnection.java deleted file mode 100755 index 36cc591c..00000000 --- a/src/main/java/com/superzanti/serversync/SyncClientConnection.java +++ /dev/null @@ -1,359 +0,0 @@ -package com.superzanti.serversync; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.UnknownHostException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Map; - -import com.google.common.collect.Maps; - -import cpw.mods.fml.common.Loader; -import cpw.mods.fml.common.ModContainer; -import cpw.mods.fml.relauncher.Side; -import cpw.mods.fml.relauncher.SideOnly; - -@SideOnly(Side.CLIENT) -public class SyncClientConnection implements Runnable { - - private static Socket socket; - private static ObjectInputStream ois; - private static ObjectOutputStream oos; - private static InetAddress host = null; - - private static boolean errorInUpdates; - private static boolean updateHappened; - private static boolean checkFinished; - - protected SyncClientConnection() { - - errorInUpdates = false; - updateHappened = false; - checkFinished = false; - - } - - @Override - public void run() { - // use the ip address of the server to get the host - try { - host = InetAddress.getByName(ServerSyncRegistry.SERVER_IP); - } catch (UnknownHostException e) { - ServerSyncRegistry.logger.error("Exception caught! - " + e); - e.printStackTrace(); - errorInUpdates = true; - } - socket = null; - oos = null; - ois = null; - SyncClient.updateScreenWorking(1, "Connecting to server..."); - try { - // establish socket connection to server - ServerSyncRegistry.logger.info("Establishing a socket connection to the server..."); - - // socket = new Socket(host.getHostName(), - // ServerSyncRegistry.SERVER_PORT); - socket = new Socket(); - socket.connect(new InetSocketAddress(host.getHostName(), ServerSyncRegistry.SERVER_PORT), 5000); - - SyncClient.updateScreenWorking(2, "Socket established..."); - - // write to socket using ObjectOutputStream - ServerSyncRegistry.logger.info("Creaing input/output streams..."); - oos = new ObjectOutputStream(socket.getOutputStream()); - ois = new ObjectInputStream(socket.getInputStream()); - - SyncClient.updateScreenWorking(3, "Checking to see if updates are needed..."); - oos.writeObject(ServerSyncRegistry.SECURE_CHECK); - oos.flush(); - String lastUpdate = (String) ois.readObject(); - ServerSyncRegistry.logger.info("Our version is:" + ServerSyncRegistry.LAST_UPDATE); - ServerSyncRegistry.logger.info("The server's version is:" + lastUpdate); - - oos.writeObject(ServerSyncRegistry.SECURE_CHECKMODS); - oos.flush(); - String serverModList = (String) ois.readObject(); - Map clientModList_ = Maps.newHashMap(Loader.instance().getIndexedModList()); - Map clientModList = Maps.newHashMap(Loader.instance().getIndexedModList()); - for (Map.Entry modEntry : clientModList_.entrySet()) { - Path modPath = Paths.get(modEntry.getValue().getSource().getAbsolutePath()); - Path rootPath = Paths.get("").toAbsolutePath(); - String relativeModPath = "./" + rootPath.relativize(modPath); - if (ServerSyncRegistry.IGNORE_LIST.contains(relativeModPath.replace('\\', '/'))) { - clientModList.remove(modEntry.getKey()); - } - } - ServerSyncRegistry.logger.info("Syncable client mods are: " + clientModList.toString()); - ServerSyncRegistry.logger.info("Syncable server mods are: " + serverModList.toString()); - if (!serverModList.toString().equals(clientModList.toString())) { - ServerSyncRegistry.logger - .info("The mods between server and client are incompatable... Force updating..."); - } - - if (!lastUpdate.equals(ServerSyncRegistry.LAST_UPDATE) - || !serverModList.toString().equals(clientModList.toString())) { - - ServerSyncRegistry.logger.info("Sending requests to Socket Server..."); - - // get all files on server - ServerSyncRegistry.logger.info("Getting the files on the server..."); - oos.writeObject(ServerSyncRegistry.SECURE_RECURSIVE); - oos.flush(); - // read the server response message - ArrayList fileTree = new ArrayList(); - fileTree = (ArrayList) ois.readObject(); - ServerSyncRegistry.logger.info(fileTree); - - SyncClient.updateScreenWorking(4, "Got filetree from server..."); - - // get all the files at home so we can update the progress bar - ArrayList allList = new ArrayList(); - allList.addAll(dirContents("./mods")); - allList.addAll(dirContents("./config")); - - SyncClient.updateScreenWorking(5, "Got filetree from client..."); - - ServerSyncRegistry.logger.info("Ignoring: " + ServerSyncRegistry.IGNORE_LIST); - - // run calculations to figure out how big the bar is - float numberOfFiles = allList.size() + fileTree.size(); - float percentScale = numberOfFiles / 92; - float currentPercent = 0; - - for (String singleFile : fileTree) { - currentPercent++; - SyncClient.updateScreenWorking((int) (5 + (currentPercent / percentScale)), - "Checking server's " + singleFile.replace('\\', '/')); - File f = new File(singleFile.replace('\\', '/')); - if (f.exists() && !f.isDirectory()) { - oos.writeObject(ServerSyncRegistry.SECURE_CHECKSUM); - oos.flush(); - oos.writeObject(singleFile.replace('\\', '/')); - oos.flush(); - String serverChecksum = (String) ois.readObject(); - // if the checksums do not match, update the file - if (!Md5.md5String(f).equals(serverChecksum)) { - if (ServerSyncRegistry.IGNORE_LIST.contains(singleFile.replace('\\', '/'))) { - ServerSyncRegistry.logger.info("Ignoring: " + singleFile.replace('\\', '/')); - } else { - ServerSyncRegistry.logger - .info(singleFile.replace('\\', '/') + " Does not match... Updating..."); - ServerSyncRegistry.logger.info("Server Checksum: " + serverChecksum); - ServerSyncRegistry.logger.info("Our Checksum: " + Md5.md5String(f)); - oos.writeObject(ServerSyncRegistry.SECURE_UPDATE); - oos.flush(); - oos.writeObject(singleFile.replace('\\', '/')); - oos.flush(); - - SyncClient.updateScreenWorking((int) (5 + (currentPercent / percentScale)), - "Updating " + singleFile.replace('\\', '/')); - - // download the file - File updated = new File(singleFile.replace('\\', '/')); - // updated.deleteOnExit(); - // ofcourse that didn't work - updated.getParentFile().mkdirs(); - FileOutputStream wr = new FileOutputStream(updated); - byte[] outBuffer = new byte[socket.getReceiveBufferSize()]; - int bytesReceived = 0; - while ((bytesReceived = ois.read(outBuffer)) > 0) { - wr.write(outBuffer, 0, bytesReceived); - } - wr.flush(); - wr.close(); - reinitConn(); - updateHappened = true; - } - } else { - ServerSyncRegistry.logger.info("We have a match! " + singleFile.replace('\\', '/')); - } - } else { - if (ServerSyncRegistry.IGNORE_LIST.contains(singleFile.replace('\\', '/'))) { - ServerSyncRegistry.logger.info("Ignoring: " + singleFile.replace('\\', '/')); - } else { - ServerSyncRegistry.logger - .info(singleFile.replace('\\', '/') + " Does not exist... Updating..."); - oos.writeObject(ServerSyncRegistry.SECURE_UPDATE); - oos.flush(); - oos.writeObject(singleFile.replace('\\', '/')); - oos.flush(); - - SyncClient.updateScreenWorking((int) (5 + (currentPercent / percentScale)), - "Updating " + singleFile.replace('\\', '/')); - - // download the file - File updated = new File(singleFile.replace('\\', '/')); - // updated.deleteOnExit(); - // ofcourse that didn't work - updated.getParentFile().mkdirs(); - FileOutputStream wr = new FileOutputStream(updated); - byte[] outBuffer = new byte[socket.getReceiveBufferSize()]; - int bytesReceived = 0; - while ((bytesReceived = ois.read(outBuffer)) > 0) { - wr.write(outBuffer, 0, bytesReceived); - } - wr.flush(); - wr.close(); - reinitConn(); - updateHappened = true; - } - } - } - - for (String singleFile : allList) { - currentPercent++; - String nURL = singleFile.replace('\\', '/'); - String fileNameSaveable = nURL.replace("/", "_$_").replaceFirst(".", "").replaceFirst("_$_", ""); - - SyncClient.updateScreenWorking((int) (5 + (currentPercent / percentScale)), - "Checking client's " + nURL); - - ServerSyncRegistry.logger.info("Checking client's files against the server's..."); - oos.writeObject(ServerSyncRegistry.SECURE_EXISTS); - oos.flush(); - oos.writeObject(nURL); - oos.flush(); - - // check for files that need to be deleted - String doesExist = (String) ois.readObject(); - - if (ServerSyncRegistry.IGNORE_LIST.contains(nURL)) { - ServerSyncRegistry.logger.info("Ignoring: " + nURL); - } else { - if (doesExist.equalsIgnoreCase("false")) { - ServerSyncRegistry.logger.info(nURL + " Does not match... Deleting..."); - SyncClient.updateScreenWorking((int) (5 + (currentPercent / percentScale)), - "Deleting client's " + nURL); - - - File deleteMe = new File(nURL); - if (!deleteMe.delete()) { - deleteMe.deleteOnExit(); // For when it gets fixed - deleteMe = new File( - "mods/serversync_delete/" + fileNameSaveable); - ServerSyncRegistry.logger.info("Attempting to create reference " + nURL +" in: " +deleteMe.getAbsolutePath()); - - deleteMe.getParentFile().mkdirs(); - FileOutputStream wr = new FileOutputStream(deleteMe); - wr.write("D".getBytes(), 0, 1); - wr.flush(); - wr.close(); - } - updateHappened = true; - } - // reinitConn(); - } - } - // I found that these lines are not needed since the client - // updates server sync anyway. - // ServerSyncRegistry.config.getCategory("StorageVariables").get("LAST_UPDATE").set(lastUpdate); - // ServerSyncRegistry.config.save(); - } else { - SyncClient.updateScreenWorking(50, "No Updates Needed :D"); - ServerSyncRegistry.logger.info("No Updates Needed"); - } - - SyncClient.updateScreenWorking(98, "Telling Server to Exit..."); - - ServerSyncRegistry.logger.info("Update Complete! Have a nice day!"); - oos.writeObject(ServerSyncRegistry.SECURE_EXIT); - oos.flush(); - } catch (Exception e) { - ServerSyncRegistry.logger.error("Exception caught! - " + e); - e.printStackTrace(); - errorInUpdates = true; - } finally { - try { - SyncClient.updateScreenWorking(99, "Closing connections..."); - - if (oos != null) - oos.close(); - if (ois != null) - ois.close(); - if (socket != null) - socket.close(); - } catch (IOException e) { - ServerSyncRegistry.logger.error("Exception caught! - " + e); - e.printStackTrace(); - errorInUpdates = true; - } // close resources here! - ServerSyncRegistry.logger.info("All of serversync's sockets to the server have been closed."); - } - - SyncClient.updateScreenWorking(100, "Finished!"); - try { - Thread.sleep(100); - } catch (InterruptedException e) { - ServerSyncRegistry.logger.error("Exception caught! - " + e); - e.printStackTrace(); - } - checkFinished = true; - return; - } - - private static ArrayList dirContents(String dir) { - ServerSyncRegistry.logger.info("Getting all of " + dir.replace('\\', '/') + "'s folder contents"); - File f = new File(dir); - File[] files = f.listFiles(); - ArrayList dirList = new ArrayList(); - // Loop through all the directories and only add to the list if it's a - // file - for (File file : files) { - if (file.isDirectory()) { - dirList.addAll(dirContents(file.getPath())); - } else { - dirList.add(file.toString()); - } - } - return dirList; - } - - private static void reinitConn() throws Exception { - ServerSyncRegistry.logger.info("Reinitializing the connection..."); - oos.flush(); - // close our resources and set values to null - if (oos != null) - oos.close(); - if (ois != null) - ois.close(); - if (socket != null) - socket.close(); - socket = null; - oos = null; - ois = null; - // socket = new Socket(host.getHostName(), - // ServerSyncRegistry.SERVER_PORT); - socket = new Socket(); - socket.connect(new InetSocketAddress(host.getHostName(), ServerSyncRegistry.SERVER_PORT), 5000); - // write to socket using ObjectOutputStream - oos = new ObjectOutputStream(socket.getOutputStream()); - ois = new ObjectInputStream(socket.getInputStream()); - ServerSyncRegistry.logger.info("Sending requests to Socket Server..."); - } - - protected static boolean getErrors() { - return errorInUpdates; - } - - protected static boolean getUpdates() { - return updateHappened; - } - - protected static boolean getFinished() { - return checkFinished; - } - - protected static void setFinished(boolean newFinished) { - checkFinished = newFinished; - return; - } - -} diff --git a/src/main/java/com/superzanti/serversync/SyncConfig.java b/src/main/java/com/superzanti/serversync/SyncConfig.java new file mode 100644 index 00000000..41e19877 --- /dev/null +++ b/src/main/java/com/superzanti/serversync/SyncConfig.java @@ -0,0 +1,184 @@ +package com.superzanti.serversync; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import com.superzanti.serversync.util.PathUtils; +import com.superzanti.serversync.util.MCConfigReader.MCCArray; +import com.superzanti.serversync.util.MCConfigReader.MCCElement; +import com.superzanti.serversync.util.MCConfigReader.MCCReader; + +import cpw.mods.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.common.config.ConfigCategory; +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.common.config.Property; + +public class SyncConfig { + public static Configuration config; + public static String SERVER_IP; + public static int SERVER_PORT; + public static int MINECRAFT_PORT; + public static String SECURE_CHECK; + public static String SECURE_CHECKMODS; + public static String SECURE_RECURSIVE; + public static String SECURE_CHECKSUM; + public static String SECURE_UPDATE; + public static String SECURE_EXISTS; + public static String SECURE_EXIT; + public static Boolean PUSH_CLIENT_MODS; + public static final String GET_CONFIG = "GIMME"; + public static List ClientMods = new ArrayList(); + public static List IGNORE_LIST; + public static List INCLUDE_LIST; + public static String LAST_UPDATE; + private static Property ignoreList; + private static Property includeList; + public static boolean pullServerConfig = true; + public static boolean configPresent = false; + + /** + * Loads/Initializes config parameters from serversync.cfg + * + * @param PreEvent + * forge pre-initialization event + */ + public static void init(FMLPreInitializationEvent PreEvent) { + config = new Configuration(PreEvent.getSuggestedConfigurationFile()); + try { + setupConfig(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static void init(File configFile) { + config = new Configuration(configFile); + try { + setupConfig(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static void getServerDetails(Path configFile) throws IOException { + //TODO read as proper file format? + MCCReader cReader = new MCCReader(Files.newBufferedReader(configFile)); + MCCArray eArray = new MCCArray(); + MCCElement element; + while ((element = cReader.readNextElement()) != null) { + eArray.add(element); + } + cReader.close(); + + MINECRAFT_PORT = eArray.getElementByName("MINECRAFT_PORT").getInt(); + SERVER_IP = eArray.getElementByName("SERVER_IP").getString(); + SERVER_PORT = eArray.getElementByName("SERVER_PORT").getInt(); + SECURE_CHECK = eArray.getElementByName("SECURE_CHECK").getString(); + SECURE_CHECKMODS = eArray.getElementByName("SECURE_CHECKMODS").getString(); + SECURE_RECURSIVE = eArray.getElementByName("SECURE_RECURSIVE").getString(); + SECURE_CHECKSUM = eArray.getElementByName("SECURE_CHECKSUM").getString(); + SECURE_UPDATE = eArray.getElementByName("SECURE_UPDATE").getString(); + SECURE_EXISTS = eArray.getElementByName("SECURE_EXISTS").getString(); + SECURE_EXIT = eArray.getElementByName("SECURE_EXIT").getString(); + LAST_UPDATE = eArray.getElementByName("LAST_UPDATE").getString(); + PUSH_CLIENT_MODS = eArray.getElementByName("PUSH_CLIENT_MODS").getBoolean(); + IGNORE_LIST = eArray.getElementByName("MOD_IGNORE_LIST").getList(); + INCLUDE_LIST = eArray.getElementByName("CONFIG_INCLUDE_LIST").getList(); + + System.out.println("finished loading config"); + } + + private static void setupConfig() throws IOException { + //TODO add accept client mods for client config file + config.load(); + // Attempt to reset config file if old value is detected + if (config.hasCategory("ignoredfiles")) { + ServerSync.logger.info("Resetting config file"); + config.removeCategory(new ConfigCategory("ignoredfiles")); + config.removeCategory(new ConfigCategory(Configuration.CATEGORY_GENERAL)); + config.removeCategory(new ConfigCategory("gui")); + } + SERVER_IP = config.getString("SERVER_IP", "ServerConnection", "127.0.0.1", "The IP address of the server"); + SERVER_PORT = config.getInt("SERVER_PORT", "ServerConnection", 38067, 1, 49151, + "The port that your server will be serving on"); + MINECRAFT_PORT = config.getInt("MINECRAFT_PORT", "ServerConnection", 25565, 1, 49151, + "The port in which the minecraft server is running, not the serversync port"); + + SECURE_CHECK = config.getString("SECURE_CHECK", "ServerEncryption", "0ba4439ee9a46d9d9f14c60f88f45f87", + "The check command security key phrase"); + SECURE_CHECKMODS = config.getString("SECURE_CHECKMODS", "ServerEncryption", "3dd3152ae3e427aa2817df12570ea708", + "The check-mods command security key phrase"); + SECURE_RECURSIVE = config.getString("SECURE_RECURSIVE", "ServerEncryption", "f8e45531a3ea3d5c1247b004985175a4", + "The recursive command security key phrase"); + SECURE_CHECKSUM = config.getString("SECURE_CHECKSUM", "ServerEncryption", "226190d94b21d1b0c7b1a42d855e419d", + "The checksum command security key phrase"); + SECURE_UPDATE = config.getString("SECURE_UPDATE", "ServerEncryption", "3ac340832f29c11538fbe2d6f75e8bcc", + "The update command security key phrase"); + SECURE_EXISTS = config.getString("SECURE_EXISTS", "ServerEncryption", "e087923eb5dd1310f5f25ddd5ae5b580", + "The exists command security key phrase"); + SECURE_EXIT = config.getString("SECURE_EXIT", "ServerEncryption", "f24f62eeb789199b9b2e467df3b1876b", + "The exit command security key phrase"); + + PUSH_CLIENT_MODS = config.getBoolean("PUSH_CLIENT_MODS", Configuration.CATEGORY_GENERAL, false, + "set true to push client side mods from clientmods directory, set on server"); + + ignoreList = config.get("Rules", "MOD_IGNORE_LIST", new String[]{}, + "These mods are ignored by serversync, list auto updates with mods added to the clientmods directory."); + + includeList = config.get("Rules", "CONFIG_INCLUDE_LIST", new String[]{}, + "These configs are included, by default configs are not synced."); + + if (PUSH_CLIENT_MODS) { + String[] oldList = ignoreList.getStringList(); + Path clientMods = Paths.get("clientmods/"); + if (Files.exists(clientMods)) { + ArrayList files = PathUtils.fileListDeep(clientMods); + ArrayList saveableFiles = new ArrayList(); + + for (Path path : files) { + boolean found = false; + String saveable = path.getFileName().toString(); + // Duplicate check + for (String oldPath : oldList) { + if (oldPath.equals(saveable)) { + found = true; + break; + } + } + if (!found) { + // file not found in ignore list + saveableFiles.add(saveable); + } + } + + for (String fileName : oldList) { + // add in previous entries + saveableFiles.add(fileName); + } + // for the lulz, should sort files with mods first followed by configs + Collections.sort(saveableFiles); + Collections.reverse(saveableFiles); + + ignoreList.set(saveableFiles.toArray(new String[] {})); + } else { + Files.createDirectories(clientMods); + } + } + + IGNORE_LIST = Arrays.asList(ignoreList.getStringList()); + INCLUDE_LIST = Arrays.asList(includeList.getStringList()); + + LAST_UPDATE = config.getString("LAST_UPDATE", "StorageVariables", "20150608_000500", + "DO NOT EDIT THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING! (If you are a server feel free to change it as much as you want to update your clients)"); + + // loading the configuration from its file + config.save(); + } +} diff --git a/src/main/java/com/superzanti/serversync/SyncServer.java b/src/main/java/com/superzanti/serversync/SyncServer.java deleted file mode 100755 index 4961aece..00000000 --- a/src/main/java/com/superzanti/serversync/SyncServer.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.superzanti.serversync; - -import java.io.File; -import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; -import java.util.ArrayList; -import cpw.mods.fml.relauncher.SideOnly; -import cpw.mods.fml.relauncher.Side; - -@SideOnly(Side.SERVER) -public class SyncServer implements Runnable { - - //static ServerSocket variable - private static ServerSocket server; - - //this is what's in our folders - private static ArrayList allList = new ArrayList(); - - protected SyncServer(){ - ServerSyncRegistry.logger.info("Getting ./mod contents"); - allList.addAll(dirContents("./mods")); - ServerSyncRegistry.logger.info("Getting ./config contents"); - allList.addAll(dirContents("./config")); - return; - } - - @Override - public void run(){ - //create the socket server object - ServerSyncRegistry.logger.info("Creating new server socket"); - try { - server = new ServerSocket(ServerSyncRegistry.SERVER_PORT); - } catch (IOException e) { - ServerSyncRegistry.logger.info("Error occured."+e); - e.printStackTrace(); - } - //keep listens indefinitely until receives 'exit' call or program terminates - ServerSyncRegistry.logger.info("Now accepting clients..."); - while(true){ - try - { - Socket socket = server.accept(); - SyncServerConnection sc = new SyncServerConnection(socket, allList, server); - new Thread(sc).start(); - } - catch(Exception e) - { - ServerSyncRegistry.logger.info("Error occured."+e); - e.printStackTrace(); - } - } - } - - private static ArrayList dirContents(String dir) { - ServerSyncRegistry.logger.info("Getting all of " + dir.replace('\\', '/') + "'s folder contents"); - File f = new File(dir); - File[] files = f.listFiles(); - ArrayList dirList = new ArrayList(); - // Loop through all the directories and only add to the list if it's a file - for (File file : files) { - if (file.isDirectory()) { - dirList.addAll(dirContents(file.getPath())); - } else { - dirList.add(file.toString()); - } - } - return dirList; - } - -} diff --git a/src/main/java/com/superzanti/serversync/SyncServerConnection.java b/src/main/java/com/superzanti/serversync/SyncServerConnection.java deleted file mode 100755 index 749b19af..00000000 --- a/src/main/java/com/superzanti/serversync/SyncServerConnection.java +++ /dev/null @@ -1,141 +0,0 @@ -package com.superzanti.serversync; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.net.ServerSocket; -import java.net.Socket; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; - -import com.google.common.collect.Maps; - -import cpw.mods.fml.common.Loader; -import cpw.mods.fml.common.ModContainer; -import cpw.mods.fml.relauncher.SideOnly; -import cpw.mods.fml.relauncher.Side; - -@SideOnly(Side.SERVER) -public class SyncServerConnection implements Runnable { - - private static Socket clientsocket; - private static ObjectInputStream ois; - private static ObjectOutputStream oos; - private static ArrayList allList = new ArrayList(); - private static ServerSocket server; - - protected SyncServerConnection(Socket socket, ArrayList allFiles, ServerSocket theServer){ - clientsocket = socket; - allList = allFiles; - server = theServer; - ServerSyncRegistry.logger.info("Connection established with " + clientsocket); - return; - } - - @Override - public void run() { - try - { - ois = new ObjectInputStream(clientsocket.getInputStream()); - oos = new ObjectOutputStream(clientsocket.getOutputStream()); - oos.flush(); - while(true) { - - String message = (String) ois.readObject(); - ServerSyncRegistry.logger.info("Received message: "+message+" from connection "+clientsocket); - - if(message.equals(ServerSyncRegistry.SECURE_CHECK)) { - oos.writeObject(ServerSyncRegistry.LAST_UPDATE); - oos.flush(); - } - - if(message.equals(ServerSyncRegistry.SECURE_CHECKMODS)){ - Map serverModList_ = Maps.newHashMap(Loader.instance().getIndexedModList()); - Map serverModList = Maps.newHashMap(Loader.instance().getIndexedModList()); - for (Map.Entry modEntry : serverModList_.entrySet()){ - Path modPath = Paths.get(modEntry.getValue().getSource().getAbsolutePath()); - Path rootPath = Paths.get("").toAbsolutePath(); - String relativeModPath = "./" + rootPath.relativize(modPath); - if (ServerSyncRegistry.IGNORE_LIST.contains(relativeModPath.replace('\\', '/'))){ - serverModList.remove(modEntry.getKey()); - } - } - ServerSyncRegistry.logger.info("Syncable mods are: " + serverModList.toString()); - oos.writeObject((String)serverModList.toString()); - oos.flush(); - } - - if(message.equals(ServerSyncRegistry.SECURE_RECURSIVE)) { - oos.writeObject(allList); - oos.flush(); - } - - if(message.equals(ServerSyncRegistry.SECURE_CHECKSUM)) { - String theFile = (String) ois.readObject(); - File f = new File(theFile); - String serverChecksum = Md5.md5String(f); - oos.writeObject(serverChecksum); - oos.flush(); - } - - if(message.equals(ServerSyncRegistry.SECURE_UPDATE)) { - ServerSyncRegistry.logger.info("Writing file to client..."); - String theFile = (String) ois.readObject(); - File f = new File(theFile); - byte[] buff = new byte[clientsocket.getSendBufferSize()]; - int bytesRead = 0; - InputStream in = new FileInputStream(f); - while((bytesRead = in.read(buff))>0) { - oos.write(buff,0,bytesRead); - } - in.close(); - oos.flush(); - break; - } - - if(message.equals(ServerSyncRegistry.SECURE_EXISTS)) { - String theFile = (String) ois.readObject(); - File f = new File(theFile); - if(f.exists() && !f.isDirectory()) { - oos.writeObject("true"); - oos.flush(); - } - else { - oos.writeObject("false"); - oos.flush(); - } - } - - if(message.equals(ServerSyncRegistry.SECURE_EXIT)) { - break; - } - } - ServerSyncRegistry.logger.info("Connection "+clientsocket+" is closing."); - } - catch(Exception e) - { - ServerSyncRegistry.logger.info("Error occured: "+e); - e.printStackTrace(); - } finally { - try { - if(oos != null) - oos.close(); - if(ois != null) - ois.close(); - if(clientsocket !=null) - clientsocket.close(); - } catch (IOException e) { - ServerSyncRegistry.logger.info("Error occured: "+e); - e.printStackTrace(); - } - } - return; - } -} diff --git a/src/main/java/com/superzanti/serversync/gui/Console.java b/src/main/java/com/superzanti/serversync/gui/Console.java new file mode 100644 index 00000000..08e870dc --- /dev/null +++ b/src/main/java/com/superzanti/serversync/gui/Console.java @@ -0,0 +1,30 @@ +package com.superzanti.serversync.gui; + +import java.lang.reflect.InvocationTargetException; + +import javax.swing.SwingUtilities; + +import runme.Main; + +public class Console implements Runnable { + + String text = ""; + + public void updateText(String text) { + this.text = text; + try { + SwingUtilities.invokeAndWait(this); + } catch (InvocationTargetException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Override + public void run() { + Main.updateText(text); + } +} diff --git a/src/main/java/com/superzanti/serversync/gui/FileProgress.java b/src/main/java/com/superzanti/serversync/gui/FileProgress.java new file mode 100644 index 00000000..0e81d6c9 --- /dev/null +++ b/src/main/java/com/superzanti/serversync/gui/FileProgress.java @@ -0,0 +1,54 @@ +package com.superzanti.serversync.gui; + +import java.lang.reflect.InvocationTargetException; + +import javax.swing.SwingUtilities; + +import com.superzanti.lib.RefStrings; + +import runme.Main; + +public class FileProgress implements Runnable { + + StringBuilder sb; + + public void updateProgress(int progress,String fileName) { + sb = new StringBuilder(100); + sb.append("<"); + sb.append(progress); + sb.append("%> "); + sb.append("Updating: "); + sb.append(fileName); + try { + SwingUtilities.invokeAndWait(this); + } catch (InvocationTargetException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + public void resetTitle() { + sb = new StringBuilder(18); + sb.append("Serversync - " + RefStrings.VERSION); + try { + SwingUtilities.invokeAndWait(this); + } catch (InvocationTargetException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Override + public void run() { + if (sb != null) { + Main.updateFileProgress(sb.toString()); + } + } + +} diff --git a/src/main/java/com/superzanti/serversync/proxy/ClientProxy.java b/src/main/java/com/superzanti/serversync/proxy/ClientProxy.java new file mode 100644 index 00000000..f4d59e54 --- /dev/null +++ b/src/main/java/com/superzanti/serversync/proxy/ClientProxy.java @@ -0,0 +1,14 @@ +package com.superzanti.serversync.proxy; + +public class ClientProxy extends CommonProxy { + + @Override + public boolean isClient() { + return true; + } + + @Override + public boolean isServer() { + return false; + } +} \ No newline at end of file diff --git a/src/main/java/com/superzanti/serversync/CommonProxy.java b/src/main/java/com/superzanti/serversync/proxy/CommonProxy.java old mode 100755 new mode 100644 similarity index 78% rename from src/main/java/com/superzanti/serversync/CommonProxy.java rename to src/main/java/com/superzanti/serversync/proxy/CommonProxy.java index 3ca5bdf3..1c47c733 --- a/src/main/java/com/superzanti/serversync/CommonProxy.java +++ b/src/main/java/com/superzanti/serversync/proxy/CommonProxy.java @@ -1,4 +1,4 @@ -package com.superzanti.serversync; +package com.superzanti.serversync.proxy; public class CommonProxy { diff --git a/src/main/java/com/superzanti/serversync/util/Log.java b/src/main/java/com/superzanti/serversync/util/Log.java new file mode 100644 index 00000000..ce22c584 --- /dev/null +++ b/src/main/java/com/superzanti/serversync/util/Log.java @@ -0,0 +1,62 @@ +package com.superzanti.serversync.util; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class Log { + + public String fileName; + private static final String EXT = ".log"; + public StringBuilder logContent = new StringBuilder(); + + public Log(String fileName) { + this.fileName = fileName; + } + + public StringBuilder getLogBuilder() { + return this.logContent; + } + + public String getReadableContent() { + return this.logContent.toString(); + } + + /** + * Shortcut method for adding to logs string builder + * @param s + */ + public Log add(String s) { + this.logContent.append(s); + this.logContent.append("\r\n"); + return this; + } + + public Log add(int i) { + this.logContent.append(i); + this.logContent.append("\r\n"); + return this; + } + + public boolean saveLog() { + + Thread saveT = new Thread(new Runnable(){ + + Path logsDir = Paths.get("../logs"); + Path log = logsDir.resolve(fileName + EXT); + @Override + public void run() { + try { + Files.createDirectories(logsDir); + Files.write(log, logContent.toString().getBytes()); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + saveT.run(); + // May need seperate thread? + return true; + } +} diff --git a/src/main/java/com/superzanti/serversync/util/Logger.java b/src/main/java/com/superzanti/serversync/util/Logger.java new file mode 100644 index 00000000..95827114 --- /dev/null +++ b/src/main/java/com/superzanti/serversync/util/Logger.java @@ -0,0 +1,60 @@ +package com.superzanti.serversync.util; + +import java.lang.reflect.InvocationTargetException; + +import com.superzanti.serversync.gui.Console; + +/** + * Manager for serversyncs logs + * @author Rheimus + * + */ +public class Logger { + private Log fullLog; + private Log userLog; + public static final String FULL_LOG = "full"; + public static final String USER_LOG = "user"; + private Console console; + + public Logger() { + fullLog = new Log("serversync-detailed"); + userLog = new Log("serversync-ui"); + console = new Console(); + } + + public boolean save() { + fullLog.saveLog(); + return true; + } + + /** + * Shorthand for upadteLogs(string,true), updates GUI console text as well + * @param s - Text to update logs with + * @throws InterruptedException + * @throws InvocationTargetException + */ + public void updateLogs(String s) { + updateLogs(s,true); + } + + /** + * Update a specific log + * @param s - Text to update log with + * @param logToUpdate - Use Logger constants + */ + public void updateLogs(String s, String logToUpdate) { + if (logToUpdate.equals(FULL_LOG)) + fullLog.add(s); + if (logToUpdate.equals(USER_LOG)) + userLog.add(s); + } + + public void updateLogs(String s, boolean update) { + fullLog.add(s); + userLog.add(s); + + if (update) { + console.updateText(userLog.getReadableContent()); + } + } +} diff --git a/src/main/java/com/superzanti/serversync/util/MCConfigReader/MCCArray.java b/src/main/java/com/superzanti/serversync/util/MCConfigReader/MCCArray.java new file mode 100644 index 00000000..f5debc51 --- /dev/null +++ b/src/main/java/com/superzanti/serversync/util/MCConfigReader/MCCArray.java @@ -0,0 +1,21 @@ +package com.superzanti.serversync.util.MCConfigReader; + +import java.util.ArrayList; + +public class MCCArray extends ArrayList { + + + /** + * + */ + private static final long serialVersionUID = -8982760081192740589L; + + public MCCElement getElementByName(String name) { + for (MCCElement e : this) { + if (e.getName().equals(name)) { + return e; + } + } + return null; + } +} diff --git a/src/main/java/com/superzanti/serversync/util/MCConfigReader/MCCElement.java b/src/main/java/com/superzanti/serversync/util/MCConfigReader/MCCElement.java new file mode 100644 index 00000000..0171edbd --- /dev/null +++ b/src/main/java/com/superzanti/serversync/util/MCConfigReader/MCCElement.java @@ -0,0 +1,74 @@ +package com.superzanti.serversync.util.MCConfigReader; + +import java.lang.reflect.Type; +import java.util.ArrayList; + +public class MCCElement { + private final String category; + private Type type; + private String value; + private String name; + private ArrayList values; + public final boolean isArray; + + public MCCElement(String category,String type,String name,String value) { + this.category = category; + this.name = name; + setType(type); + this.isArray = false; + this.value = value; + this.values = null; + } + + public MCCElement(String category,String type,String name, ArrayList values) { + this.category = category; + this.name = name; + setType(type); + this.values = values; + this.isArray = true; + this.value = null; + } + + private void setType(String type) { + if (type.equals("B")) { + this.type = Boolean.class; + } + if (type.equals("S")) { + this.type = String.class; + } + if (type.equals("I")) { + this.type = Integer.class; + } + } + + public ArrayList getList() { + if (isArray) { + return values; + } + return null; + } + + public String getCategoryName() { + return category; + } + + public java.lang.reflect.Type getType() { + return type; + } + + public boolean getBoolean() { + return Boolean.valueOf(value); + } + + public String getName() { + return name; + } + + public String getString() { + return value; + } + + public int getInt() { + return Integer.parseInt(value); + } +} diff --git a/src/main/java/com/superzanti/serversync/util/MCConfigReader/MCCReader.java b/src/main/java/com/superzanti/serversync/util/MCConfigReader/MCCReader.java new file mode 100644 index 00000000..4633af22 --- /dev/null +++ b/src/main/java/com/superzanti/serversync/util/MCConfigReader/MCCReader.java @@ -0,0 +1,82 @@ +package com.superzanti.serversync.util.MCConfigReader; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.ArrayList; + +public class MCCReader extends BufferedReader { + + //TODO create separate server/client config files + //TODO have server send handshake secure codes and remove from clients config + + public String category; + + public MCCReader(BufferedReader read) throws IOException { + super(read); + } + + public MCCElement readNextElement() throws IOException { + String line; + while ((line = this.readLine()) != null) { + if (line.contains("#")) { + continue; + } + if (line.contains("}")) { + category = null; + continue; + } + if (line.contains("{")) { + String[] cat = line.trim().split(" "); + // Should get category name + category = cat[0]; + // Move to next line + continue; + } + if (line.contains(":") && line.contains("=")) { + String type = getType(line); + String name = getName(line); + String value = getValue(line); + return new MCCElement(category,type,name,value); + } + if (line.contains(":") && line.contains("<")) { + String type = getType(line); + String name = getName(line); + return new MCCElement(category,type,name,getValues()); + } + } + return null; + } + + private ArrayList getValues() throws IOException { + ArrayList temp = new ArrayList(); + String line; + while (true) { + line = this.readLine(); + if (line.contains(">")) { + break; + } + temp.add(line.replace(",", "").trim()); + } + return temp; + } + + private String getType(String line) { + String sub = line.substring(line.indexOf(":") - 1, line.indexOf(":")).trim(); + return sub; + } + + private String getName(String line) { + String sub; + if (line.contains("=")) { + sub = line.substring(line.indexOf(":")+1,line.indexOf("=")).trim(); + return sub; + } + sub = line.substring(line.indexOf(":")+1,line.indexOf("<")).trim(); + return sub; + } + + private String getValue(String line) { + String sub = line.substring(line.indexOf("=")+1).trim(); + return sub; + } +} diff --git a/src/main/java/com/superzanti/serversync/Md5.java b/src/main/java/com/superzanti/serversync/util/Md5.java old mode 100755 new mode 100644 similarity index 87% rename from src/main/java/com/superzanti/serversync/Md5.java rename to src/main/java/com/superzanti/serversync/util/Md5.java index 665d6b40..b56a1ab1 --- a/src/main/java/com/superzanti/serversync/Md5.java +++ b/src/main/java/com/superzanti/serversync/util/Md5.java @@ -1,40 +1,38 @@ -package com.superzanti.serversync; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.security.MessageDigest; - -public class Md5 { - - public static String md5String(File file) { - try { - InputStream fin = new FileInputStream(file); - java.security.MessageDigest md5er = MessageDigest.getInstance("MD5"); - byte[] buffer = new byte[1024]; - int read; - do { - read = fin.read(buffer); - if (read > 0) { - md5er.update(buffer, 0, read); - } - } while (read != -1); - fin.close(); - byte[] digest = md5er.digest(); - if (digest == null) { - return null; - } - String strDigest = "0x"; - for (int i = 0; i < digest.length; i++) { - strDigest += Integer.toString((digest[i] & 0xff) - + 0x100, 16).substring(1).toUpperCase(); - } - return strDigest; - } catch (Exception e) { - return null; - } - } - -} +package com.superzanti.serversync.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.security.MessageDigest; + +public class Md5 { + + public static String md5String(File file) { + try { + InputStream fin = new FileInputStream(file); + java.security.MessageDigest md5er = MessageDigest.getInstance("MD5"); + byte[] buffer = new byte[1024]; + int read; + do { + read = fin.read(buffer); + if (read > 0) { + md5er.update(buffer, 0, read); + } + } while (read != -1); + fin.close(); + byte[] digest = md5er.digest(); + if (digest == null) { + return null; + } + String strDigest = "0x"; + for (int i = 0; i < digest.length; i++) { + strDigest += Integer.toString((digest[i] & 0xff) + + 0x100, 16).substring(1).toUpperCase(); + } + return strDigest; + } catch (Exception e) { + return null; + } + } + +} diff --git a/src/main/java/com/superzanti/serversync/util/PathUtils.java b/src/main/java/com/superzanti/serversync/util/PathUtils.java new file mode 100644 index 00000000..4a5b3151 --- /dev/null +++ b/src/main/java/com/superzanti/serversync/util/PathUtils.java @@ -0,0 +1,154 @@ +package com.superzanti.serversync.util; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.stream.Stream; +/** + * Helper for working with paths and directories + * @author Rheimus + * + */ +public class PathUtils { + + private static StringBuilder pathBuilder; + + /** + * Go to last occurrence of target in path + * + * @param target + * what directory you want to go to + * @param path + * the absolute path of your current directory + * @param offset + * walk further up the chain + * @return String rebuilt up to the target directory
+ * + *
+	 * e.g. 1) target = foo, path = bar/foo/ring/ding/ 
+	 * returns: bar/foo/ 
2) target = foo, path = bar/foo/ring/ding, offset = 1 + * returns: bar/ + *
+ */ + public static String walkTo(String target, String path, int offset) { + List pathParts = getPathParts(path); + target = target.toLowerCase(); + + pathBuilder = new StringBuilder(); + int locationOfTarget = pathParts.lastIndexOf(target); + + ListIterator iter = pathParts.listIterator(); + + while (iter.hasNext()) { + String part = iter.next(); + int index = iter.nextIndex(); + if (part.equalsIgnoreCase(target) && index >= locationOfTarget) { + pathBuilder.append(part); + pathBuilder.append("/"); + System.out.println("found target"); + System.out.println(pathBuilder.toString()); + break; + } else { + pathBuilder.append(part); + pathBuilder.append("/"); + System.out.println("appended: " + part); + } + } + + if (offset > 0) { + try { + return walkUp(offset, pathBuilder.toString()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + return pathBuilder.toString(); + } + + /** + * Go up offset number of parts in path + * + * @param offset + * how far to walk up the path + * @param path + * the absolute path of your current directory + * @return String rebuilt up to the offset amount
+ * + *
+	 * e.g. offset = 2, path = bar/foo/ring/ding/ 
+	 * returns: bar/foo/
+	 *         
+ * + * @throws Exception + * if offset is longer than path + */ + public static String walkUp(int offset, String path) throws Exception { + List pathParts = getPathParts(path); + int ppLen = pathParts.size(); + pathBuilder = new StringBuilder(); + + if (offset > ppLen) { + throw new Exception("Offset is longer than path chain"); + } + + for (int i = 0; i < ppLen - offset; i++) { + pathBuilder.append(pathParts.get(i)); + pathBuilder.append("/"); + } + return pathBuilder.toString(); + } + + public static Path getMinecraftDirectory() throws IOException { + Path minecraft = Paths.get("../"); + return minecraft.toRealPath(); + } + + private static List getPathParts(String path) { + path = path.replace('\\', '/'); + String[] pp = path.split("/"); + List ppl = new ArrayList(); + for (String s : pp) { + ppl.add(s.toLowerCase()); + } + return ppl; + } + + public static File[] fileList(String directory) { + File contents = new File(directory); + return contents.listFiles(); + } + + public static ArrayList fileListDeep(Path dir) { + try { + if (Files.exists(dir)) { + Stream ds = Files.walk(dir); + + ArrayList dirList = new ArrayList(); + + Iterator it = ds.iterator(); + while (it.hasNext()) { + Path tp = it.next(); + // discard directories + if (!Files.isDirectory(tp)) { + dirList.add(tp); + } + } + ds.close(); + return dirList; + } else { + return null; + } + + } catch (IOException e) { + System.out.println("Could not traverse directory"); + } + return null; + } +} diff --git a/src/main/java/com/superzanti/serversync/util/Server.java b/src/main/java/com/superzanti/serversync/util/Server.java new file mode 100644 index 00000000..ed803712 --- /dev/null +++ b/src/main/java/com/superzanti/serversync/util/Server.java @@ -0,0 +1,296 @@ +package com.superzanti.serversync.util; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import com.superzanti.serversync.ClientWorker; +import com.superzanti.serversync.SyncConfig; +import com.superzanti.serversync.gui.Console; +import com.superzanti.serversync.gui.FileProgress; + +import runme.Main; + +/** + * Interacts with a server running serversync + * + * @author Rheimus + * + */ +public class Server { + + public final String IP_ADDRESS; + public final int PORT; + private ObjectOutputStream oos = null; + private ObjectInputStream ois = null; + private Socket clientSocket = null; + private InetAddress host = null; + private Logger logs; + + public Server(ClientWorker caller, String ip, int port) { + IP_ADDRESS = ip; + PORT = port; + logs = caller.getLogger(); + } + + public boolean connect() { + Console con = new Console(); + try { + host = InetAddress.getByName(IP_ADDRESS); + } catch (UnknownHostException e) { + con.updateText("Could not connect to host: " + IP_ADDRESS); + return false; + } + + logs.updateLogs("Establishing a socket connection to the server...", Logger.FULL_LOG); + clientSocket = new Socket(); + + logs.updateLogs("< Connecting to server >"); + try { + clientSocket.connect(new InetSocketAddress(host.getHostName(), PORT), 5000); + } catch (IOException e) { + con.updateText("Could not connect to server at: " + IP_ADDRESS + ":" + PORT); + return false; + } + + // write to socket using ObjectOutputStream + logs.updateLogs("Creating input/output streams...", Logger.FULL_LOG); + try { + oos = new ObjectOutputStream(clientSocket.getOutputStream()); + ois = new ObjectInputStream(clientSocket.getInputStream()); + } catch (IOException e) { + logs.updateLogs("Failed to obtain input/output streams from client", Logger.FULL_LOG); + return false; + } + + return true; + } + + /** + * Terminates the listener thread on the server for this client + * @throws IOException + */ + public void exit() throws IOException { + logs.updateLogs("Telling server to exit...", Logger.FULL_LOG); + oos.writeObject(SyncConfig.SECURE_EXIT); + oos.flush(); + } + + /** + * Releases resources related to this server instance, MUST call this when interaction is finished if a server is opened + * @return + */ + public boolean close() { + logs.updateLogs("Closing connections...", Logger.FULL_LOG); + try { + if (oos != null) + oos.close(); + if (ois != null) + ois.close(); + if (clientSocket != null && !clientSocket.isClosed()) + clientSocket.close(); + } catch (IOException e) { + logs.updateLogs("Exception caught! - " + e.getMessage(), Logger.FULL_LOG); + return false; + } + logs.updateLogs("All of serversync's sockets to the server have been closed.", Logger.FULL_LOG); + return true; + } + + public boolean reinitConnection() { + logs.updateLogs("Reinitializing the connection...", Logger.FULL_LOG); + try { + oos.flush(); + // close our resources and set values to null + if (oos != null) + oos.close(); + if (ois != null) + ois.close(); + if (clientSocket != null && !clientSocket.isClosed()) + clientSocket.close(); + clientSocket = null; + oos = null; + ois = null; + } catch (IOException e) { + logs.updateLogs("Failed to reset streams: " + e.getMessage(), Logger.FULL_LOG); + return false; + } + + clientSocket = new Socket(); + try { + clientSocket.connect(new InetSocketAddress(host.getHostName(), SyncConfig.SERVER_PORT), 5000); + } catch (IOException e) { + logs.updateLogs("Could not connect to server at: " + IP_ADDRESS + ":" + PORT); + return false; + } + + logs.updateLogs("Sending requests to socket server...", Logger.FULL_LOG); + try { + oos = new ObjectOutputStream(clientSocket.getOutputStream()); + ois = new ObjectInputStream(clientSocket.getInputStream()); + } catch (IOException e) { + logs.updateLogs("Failed to obtain streams: " + e.getMessage(), Logger.FULL_LOG); + return false; + } + return true; + } + + @SuppressWarnings("unchecked") + public boolean isUpdateNeeded(List clientMods) { + try { + // TODO check last updated information + + oos.writeObject(SyncConfig.SECURE_CHECKMODS); + oos.flush(); + // List of mod names + ArrayList serverModNames = (ArrayList) ois.readObject(); + ArrayList clientModNames = SyncFile.listModNames(clientMods); + + // Remove ignored mods from mod list + logs.updateLogs("Syncable client mods are: " + clientModNames.toString(), Logger.FULL_LOG); + logs.updateLogs("Syncable server mods are: " + serverModNames.toString(), Logger.FULL_LOG); + + serverModNames.removeAll(clientModNames); + + if (serverModNames.size() == 0) { + return false; + } + } catch (Exception e) { + logs.updateLogs("Failed to get update information: " + e.getMessage(), Logger.FULL_LOG); + return false; + } + return true; + } + + @SuppressWarnings("unchecked") + public ArrayList getFiles() throws IOException { + oos.writeObject(SyncConfig.SECURE_RECURSIVE); + oos.flush(); + + try { + ArrayList serverMods = new ArrayList(); + serverMods = (ArrayList) ois.readObject(); + logs.updateLogs("Recieved server file tree", Logger.FULL_LOG); + + return serverMods; + } catch (ClassNotFoundException e) { + logs.updateLogs("Failed to read class: " + e.getMessage(), Logger.FULL_LOG); + } + return null; + } + + public boolean getConfig() throws IOException { + oos.writeObject(SyncConfig.GET_CONFIG); + oos.flush(); + + Path config = Paths.get("../config/serversync.cfg"); + Files.createDirectories(config.getParent()); + + FileOutputStream wr = new FileOutputStream(config.toFile()); + + byte[] outBuffer = new byte[clientSocket.getReceiveBufferSize()]; + int bytesReceived = 0; + + while ((bytesReceived = ois.read(outBuffer)) > 0) { + wr.write(outBuffer, 0, bytesReceived); + } + wr.flush(); + wr.close(); + reinitConnection(); + + logs.updateLogs("Sucessfully updated config"); + logs.updateLogs("Reloading config"); + SyncConfig.getServerDetails(config); + return true; + } + + public boolean modExists(SyncFile mod) throws IOException { + oos.writeObject(SyncConfig.SECURE_EXISTS); + oos.flush(); + oos.writeObject(mod.fileName); + oos.flush(); + + boolean modExists = ois.readBoolean(); + + return modExists; + } + + /** + * Sends request to server for the file stored at filePath and updates the + * current file with the returned data + * + * @param filePath + * the location of the file on the server + * @param currentFile + * the current file being worked on + * @throws IOException + * @throws Exception + */ + public boolean updateFile(String filePath, File currentFile) throws IOException, Exception { + oos.writeObject(Main.SECURE_FILESIZE); + oos.flush(); + oos.writeObject(filePath); + oos.flush(); + /* + * Path root = Paths.get("../"); Path relPath = + * root.relativize(currentFile.toPath()); currentFile = + * relPath.toFile(); + */ + + // TODO update to NIO + + long fileSize = 0l; + boolean gotFileSize = false; + FileProgress GUIUpdater = new FileProgress(); + + try { + fileSize = ois.readLong(); + gotFileSize = true; + } catch (Exception e) { + System.out.println("Could not get file size"); + } + + oos.writeObject(SyncConfig.SECURE_UPDATE); + oos.flush(); + oos.writeObject(filePath); + oos.flush(); + + currentFile.getParentFile().mkdirs(); + FileOutputStream wr = new FileOutputStream(currentFile); + + byte[] outBuffer = new byte[clientSocket.getReceiveBufferSize()]; + int bytesReceived = 0; + + double progress = 0; + double byteP = 0; + double factor = 0; + + while ((bytesReceived = ois.read(outBuffer)) > 0) { + if (gotFileSize) { + byteP++; + factor = fileSize / bytesReceived; + progress = Math.ceil(byteP / factor * 100); + } + wr.write(outBuffer, 0, bytesReceived); + GUIUpdater.updateProgress((int)progress, currentFile.getName()); + } + GUIUpdater.resetTitle(); + wr.flush(); + wr.close(); + reinitConnection(); + + logs.updateLogs("Sucessfully updated " + currentFile.getName()); + return true; + } +} diff --git a/src/main/java/com/superzanti/serversync/util/SyncFile.java b/src/main/java/com/superzanti/serversync/util/SyncFile.java new file mode 100644 index 00000000..6609f311 --- /dev/null +++ b/src/main/java/com/superzanti/serversync/util/SyncFile.java @@ -0,0 +1,273 @@ +package com.superzanti.serversync.util; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.nio.file.DirectoryNotEmptyException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonStreamParser; +import com.superzanti.serversync.SyncConfig; + +/** + * Holds relevant information about mods obtianed through mcmod.info.
+ *
+ * Use CLIENT_MODPATH for any interactions on the client side, this will cause + *
+ * the mod to look in the clients mods/ directory regardless of where the file + *
+ * is on the server
+ *
+ * + * Useful for instance when the server sends client-side mods from the + * clientmods
+ * directory + * + * @author Rheimus + * + */ +public class SyncFile implements Serializable { + private static final long serialVersionUID = -3869215783959173682L; + public String version; + public String name; + public String fileName; + private String md5FileContents; + transient public Path MODPATH; + transient public Path CLIENT_MODPATH; + public boolean clientOnlyMod = false; + public boolean isConfig = false; + private boolean isIgnored = false; + public static final String UNKNOWN_VERSION = "unknown_version"; + public static final String UNKNOWN_NAME = "unknown_name"; + + private File serMODPATH; + private File serCLIENT_MODPATH; + + /** + * Main constructor, populates file information + * @param modPath + * @param isMod false to skip populating mod information from mcmod.info + * @throws IOException + */ + public SyncFile(Path modPath, boolean isMod) throws IOException { + MODPATH = modPath; + Path cModPath = modPath; + Path root = Paths.get("../"); + // TODO update this code chunk to be more OOP + if (modPath.toString().contains("clientmods")) { + clientOnlyMod = true; + cModPath = root.relativize(Paths.get(modPath.toString().replaceFirst("clientmods", "mods"))); + } else { + cModPath = root.relativize(cModPath); + } + CLIENT_MODPATH = cModPath; + fileName = MODPATH.getFileName().toString(); + + if (fileName.contains(".cfg")) { + isConfig = true; + isMod = false; + } + + if (isMod && isZipJar(fileName)) { + populateModInformation(); + } + + if (version == null) { + version = SyncFile.UNKNOWN_VERSION; + // Get hashed file contents if version could not be obtained used in compare functionality + md5FileContents = Md5.md5String(MODPATH.toFile()); + } + if (name == null) { + name = SyncFile.UNKNOWN_NAME; + } + } + + /** + * Shortcut constructor that assumes the created file is a mod + * + * @param modPath + * - Path to the mod + * @throws IOException + */ + public SyncFile(Path modPath) throws IOException { + this(modPath, true); + } + + /** + * Returns true if the configs ignore list contains the file name of this SyncFile + * @return true if ignored, false otherwise + */ + public boolean isSetToIgnore() { + if (SyncConfig.IGNORE_LIST.contains(fileName)) { + isIgnored = true; + } + return isIgnored; + } + + /** + * Only used for config files, set based on serversyncs rule list INCLUDE_LIST + * @return true if the configs include list contains this SyncFiles file name + */ + public boolean isIncluded() { + List includes = SyncConfig.INCLUDE_LIST; + // Strip witespace + String cleanedName = fileName.replaceAll(" ", ""); + if (includes.contains(cleanedName)) { + return true; + } + return false; + } + + /** + * Tests file to see if it is a packaged/zipped file + * @param fileName + * @return true if file is a package + */ + private boolean isZipJar(String fileName) { + // TODO make a better way to do this, perhaps use failure of javas ZippedFile class + boolean isZip = false; + if (fileName.endsWith(".zip") || fileName.endsWith(".jar")) { + isZip = true; + } + + return isZip; + } + + private void populateModInformation() throws IOException { + if (Files.exists(MODPATH)) { + JarFile packagedMod = new JarFile(MODPATH.toFile()); + JarEntry modInfo = packagedMod.getJarEntry("mcmod.info"); + if (modInfo != null) { + InputStream is = packagedMod.getInputStream(modInfo); + InputStreamReader read = new InputStreamReader(is); + JsonStreamParser parser = new JsonStreamParser(read); + + while (parser.hasNext()) { + JsonElement element = parser.next(); + if (element.isJsonArray()) { + // This will be the opening document array + JsonArray jArray = element.getAsJsonArray(); + + // Get each array of objects + // array 1 {"foo":"bar"}, array 2 {"foo":"bar"} + for (JsonElement jObject : jArray) { + // This will contain all of the mod info + JsonObject info = jObject.getAsJsonObject(); + version = info.get("version").getAsString(); + name = info.get("name").getAsString(); + } + } + } + read.close(); + is.close(); + packagedMod.close(); + + } + } + } + + /** + * Compares mod versions from mcmod.info or compares file contents if version could not be found + * + * @param serversMod + * - servers version of the mod + * @return True if versions or content are the same
+ * False if versions are different or if version is unknown and contents could not be read + */ + public boolean compare(SyncFile serversMod) { + if (!serversMod.version.equals(SyncFile.UNKNOWN_VERSION)) { + return this.version.equals(serversMod.version); + } else if (md5FileContents != null && serversMod.md5FileContents != null) { + return this.md5FileContents.equals(serversMod.md5FileContents); + } + return false; + } + + /** + * Deletes the file this SyncFile refers to + * @return true if file deleted successfully + * @throws IOException + */ + public boolean delete() throws IOException { + boolean success = false; + try { + success = Files.deleteIfExists(MODPATH); + } catch (DirectoryNotEmptyException e) { + System.out.println("Trying to delete a directory, are you sure this is what you want to do?"); + System.out.println(e.getMessage()); + } + return success; + } + + public void deleteOnExit() { + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + try { + Files.delete(MODPATH); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + } + + public static ArrayList listModNames(List... modLists) { + if (modLists != null && modLists.length > 0) { + ArrayList names = new ArrayList(); + int len = modLists.length; + for (int i = 0; i < len; i++) { + for (SyncFile mod : modLists[i]) { + names.add(mod.fileName); + } + } + return names; + } + return null; + } + + /** + * This is intended to be a shortcut for creating a bunch of SyncFiles from the output of PathUtils fileListDeep + * @param paths a list of paths to convert to SyncFiles + * @return A list of SyncFiles + * @throws IOException + */ + public static ArrayList parseList(List paths) throws IOException { + ArrayList mods = new ArrayList(); + for (Path path : paths) { + mods.add(new SyncFile(path)); + } + return mods; + } + + /* Serialization methods */ + private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException { + is.defaultReadObject(); + if (serMODPATH != null) { + MODPATH = serMODPATH.toPath(); + } + if (serCLIENT_MODPATH != null) { + CLIENT_MODPATH = serCLIENT_MODPATH.toPath(); + } + } + + private void writeObject(ObjectOutputStream os) throws IOException { + serMODPATH = MODPATH.toFile(); + serCLIENT_MODPATH = CLIENT_MODPATH.toFile(); + os.defaultWriteObject(); + } + +} diff --git a/src/main/java/runme/Delete.java b/src/main/java/runme/Delete.java deleted file mode 100644 index e6f794ad..00000000 --- a/src/main/java/runme/Delete.java +++ /dev/null @@ -1,54 +0,0 @@ -package runme; - -import java.io.File; - -public class Delete implements Runnable { - - public static boolean finished; - - @Override - public void run() { - try { - System.out.println("Sleeping for 3 seconds"); - Thread.sleep(2000); - - File[] filesToDelete = new File[] {}; - File deleteDir = new File("serversync_delete/"); - deleteDir.mkdirs(); - - File minecraftDir = new File(""); - String useable = minecraftDir.getAbsolutePath().replace("\\", "/"); - String[] brokenOut = useable.split("/"); - int len = brokenOut.length - 1; - String newUrl = ""; - for (int i = 0; i < len; i++) { - newUrl += brokenOut[i]+"/"; - } - minecraftDir = new File(newUrl); - - System.out.println(deleteDir.getPath()); - - if (deleteDir.exists() && deleteDir.isDirectory()) { - filesToDelete = deleteDir.listFiles(); - } - - if (filesToDelete != null && filesToDelete.length > 0) { - - for (File file : filesToDelete) { - String converted = file.getName().replace("_$_", "/"); - File deleteMe = new File(minecraftDir + converted); - if (deleteMe.delete()) { - String path = deleteMe.getAbsolutePath(); - System.out.println("Successfully deleted " + path); - file.delete(); - } - } - - } - } catch (Exception e) { - e.printStackTrace(); - } - System.out.println("Finished running delete code"); - } - -} diff --git a/src/main/java/runme/Main.java b/src/main/java/runme/Main.java index ffa37ac6..d4409c98 100644 --- a/src/main/java/runme/Main.java +++ b/src/main/java/runme/Main.java @@ -1,15 +1,230 @@ package runme; +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyListener; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; + +import com.sun.glass.events.KeyEvent; +import com.superzanti.lib.RefStrings; +import com.superzanti.serversync.ClientWorker; +import com.superzanti.serversync.SyncConfig; +import com.superzanti.serversync.util.MCConfigReader.MCCReader; public class Main { - - private static Delete deleteOldMods = new Delete(); - public static void main(String[] args) throws InterruptedException, IOException { - System.out.println("Running main code"); - Thread t = new Thread(deleteOldMods); - t.start(); + public static final String SECURE_FILESIZE = "11b4278c7e5a79003db77272c1ed2cf5"; + public static final String SECURE_PUSH_CLIENTMODS = "0ad95bb1734520dc1fa3c737f8a57d91"; + + private static JTextArea TA_info = new JTextArea(); + private static JTextField TF_ipAddress = new JTextField(); + private static JTextField TF_port = new JTextField(); + private static JButton B_sync = new JButton(); + private static JFrame F_root; + private static JPanel P_serverDetails; + + private static void GUI() { + Dimension sDetailsElements = new Dimension(140, 20); + + F_root = new JFrame("Serversync " + RefStrings.VERSION); + F_root.setResizable(false); + F_root.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + F_root.setPreferredSize(new Dimension(600, 300)); + F_root.setIconImage(new ImageIcon(ClassLoader.getSystemResource("tap.png")).getImage()); + + P_serverDetails = new JPanel(); + P_serverDetails.setPreferredSize(new Dimension(143, 300)); + + B_sync.setPreferredSize(sDetailsElements); + TF_ipAddress.setPreferredSize(sDetailsElements); + TF_port.setPreferredSize(sDetailsElements); + + B_sync.setToolTipText("Starts sync process"); + B_sync.setText("Sync"); + TF_ipAddress.setOpaque(true); + TF_port.setOpaque(true); + TF_ipAddress.addKeyListener(new KeyListener() { + + @Override + public void keyTyped(java.awt.event.KeyEvent e) { + return; + } + + @Override + public void keyReleased(java.awt.event.KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_TAB) { + TF_ipAddress.transferFocus(); + } + } + + @Override + public void keyPressed(java.awt.event.KeyEvent e) { + return; + } + }); + TF_port.addKeyListener(new KeyListener() { + + @Override + public void keyTyped(java.awt.event.KeyEvent e) { + return; + } + + @Override + public void keyReleased(java.awt.event.KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + F_root.requestFocus(); + startWorker(); + } + } + + @Override + public void keyPressed(java.awt.event.KeyEvent e) { + return; + } + }); + Container pane = F_root.getContentPane(); + + JLabel ipLabel = new JLabel("IP Address"); + JLabel portLabel = new JLabel("Port"); + ipLabel.setLabelFor(TF_ipAddress); + portLabel.setLabelFor(TF_port); + + JScrollPane sp_console = new JScrollPane(TA_info); + sp_console.setPreferredSize(new Dimension(450, 300)); + + P_serverDetails.add(ipLabel); + P_serverDetails.add(TF_ipAddress); + P_serverDetails.add(portLabel); + P_serverDetails.add(TF_port); + P_serverDetails.add(B_sync); + + TA_info.setLineWrap(true); + TA_info.setWrapStyleWord(true); + TA_info.setOpaque(true); + TA_info.setEditable(false); + pane.add(sp_console, BorderLayout.LINE_END); + pane.add(P_serverDetails, BorderLayout.LINE_START); + + // Listeners + B_sync.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + startWorker(); + } + }); + + // Display window + F_root.pack(); + F_root.setLocationRelativeTo(null); + F_root.setVisible(true); + + Path config = Paths.get("../config/serversync.cfg"); + if (Files.exists(config)) { + updateText("serversync.cfg found"); + System.out.println("attempting to init config file: " + config.toAbsolutePath().toString()); + // ServerSyncConfig.init(config.toFile()); + try { + SyncConfig.getServerDetails(config); + } catch (IOException e) { + e.printStackTrace(); + return; + } + TF_ipAddress.setText(SyncConfig.SERVER_IP); + TF_port.setText(Integer.toString(SyncConfig.SERVER_PORT)); + SyncConfig.pullServerConfig = false; + SyncConfig.configPresent = true; + TF_port.requestFocus(); + } + } + + public static void main(String[] args) throws InterruptedException, IOException { + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + GUI(); + } + + }); + } + + public static void updateText(String string) { + // TODO UI appears to freeze under strain + TA_info.setText(string); + } + + public static void updateProgress(int progress) { + B_sync.setText(progress + "%"); + } + + public static void updateFileProgress(String progress) { + F_root.setTitle(progress); + } + + private static void startWorker() { + boolean error = false; + Thread t; + if (TF_ipAddress.getText().equals("") || TF_port.getText().equals("")) { + updateText("No config found, requesting details"); + if (TF_ipAddress.getText().equals("")) { + String serverIP = (String) JOptionPane.showInputDialog("Server IP address"); + TF_ipAddress.setText(serverIP); + } + if (TF_port.getText().equals("")) { + String serverPort = (String) JOptionPane.showInputDialog("Server Port (numbers only)"); + TF_port.setText(serverPort); + } + SyncConfig.pullServerConfig = true; + } + + int port = 0; + try { + port = Integer.parseInt(TF_port.getText()); + if (!(port <= 49151 && port > 0)) { + error = true; + updateText("Port out of range, valid range: 1 - 49151"); + } + } catch (NumberFormatException e) { + error = true; + updateText("Invalid port"); + } + SyncConfig.SERVER_PORT = port; + SyncConfig.SERVER_IP = TF_ipAddress.getText(); + + if (!error) { + updateText("Starting update process..."); + toggleButton(); + t = new Thread(new ClientWorker()); + t.start(); + } + + } + + public static void toggleButton() { + if (B_sync.isEnabled()) { + B_sync.setEnabled(false); + } else { + B_sync.setEnabled(true); + B_sync.setText("Sync"); + } } } diff --git a/src/main/resources/tap.png b/src/main/resources/tap.png new file mode 100644 index 00000000..1dc23444 Binary files /dev/null and b/src/main/resources/tap.png differ