();
+ 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