package co.forsaken.projectindigo.data;
import java.awt.Desktop;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JOptionPane;
import lombok.Data;
import org.apache.commons.io.FileUtils;
import co.forsaken.projectindigo.IndigoLauncher;
import co.forsaken.projectindigo.data.tokens.ServerToken;
import co.forsaken.projectindigo.gui.LoginPanel;
import co.forsaken.projectindigo.gui.MainPanel;
import co.forsaken.projectindigo.gui.ProgressPanel;
import co.forsaken.projectindigo.gui.ServerBasePanel;
import co.forsaken.projectindigo.gui.SettingsPanel;
import co.forsaken.projectindigo.log.LogManager;
import co.forsaken.projectindigo.mclaunch.MinecraftLauncher;
import co.forsaken.projectindigo.utils.DirectoryLocations;
import co.forsaken.projectindigo.utils.NbtUtils;
import co.forsaken.projectindigo.utils.ServerLoader;
import co.forsaken.projectindigo.utils.atlauncher.AtLauncherServerLoader;
import co.forsaken.projectindigo.utils.ftb.FtbServerLoader;
import co.forsaken.projectindigo.utils.ftb.tokens.Artifact;
import co.forsaken.projectindigo.utils.ftb.tokens.PackToken;
import co.forsaken.projectindigo.utils.ftb.tokens.PackToken.Library;
import co.forsaken.projectindigo.utils.technic.TechnicServerLoader;
import co.forsaken.projectindigo.utils.tokens.NbtServerToken;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
@Data public class Server {
private ServerToken token;
private String url;
private String desc;
private HashMap<String, Mod> modList;
private ServerLoader loader;
private String launchArgs;
private String jarOrder = "";
public boolean online;
public Server(ServerToken _token) {
token = _token;
if (token.modpackType.equalsIgnoreCase("ftb")) {
loader = new FtbServerLoader(this);
} else if (token.modpackType.equalsIgnoreCase("atlaunch")) {
loader = new AtLauncherServerLoader(this);
} else if (token.modpackType.equalsIgnoreCase("technic")) {
loader = new TechnicServerLoader(this);
}
mkdirs();
}
public void finishedLoading() {
LogManager.info(token.friendlyName + " has been fully hooked into " + token.modpackType);
}
public void cleanup() {
try {
FileUtils.deleteDirectory(getMinecraftDir());
} catch (IOException e) {}
mkdirs();
}
private void mkdirs() {
if (!getMinecraftDir().exists()) getMinecraftDir().mkdirs();
if (!getModsDir().exists()) getModsDir().mkdir();
if (!getConfigDir().exists()) getConfigDir().mkdir();
if (!getLibraryDir().exists()) getLibraryDir().mkdir();
if (!getResourceDir().exists()) getResourceDir().mkdir();
if (!getJarModsDir().exists()) getJarModsDir().mkdir();
}
public File getLockFile() {
return new File(getMinecraftDir(), "version.dat");
}
public String getDownloadLocation() {
if (loader == null) { return null; }
if (loader.isWholeDownload()) {
return loader.getDownloadUrl(this);
} else {
return loader.getDownloadUrl(this, modList.get(modList.keySet().toArray(new String[] {})[0]).getName());
}
}
public boolean needsDownload() {
boolean versionUpdate = false;
if (getLockFile().exists()) {
try {
if (FileUtils.readFileToString(getLockFile()).equalsIgnoreCase(getToken().version)) {
return false;
} else {
versionUpdate = true;
}
} catch (IOException e) {
e.printStackTrace();
}
}
if (versionUpdate) {
JOptionPane.showMessageDialog(null, "There has been an update to the modpack that the server uses. \nRedownloading pack to bring you up to date");
}
return true;
}
public String getPathFriendlyName() {
return token.friendlyName.replace(" ", "_").replace(".", "_");
}
private File mcDir, modsDir, configDir, libsDir, jarDir, nativesDir, resourceDir;
public void updateDirLinks() {
mcDir = null;
modsDir = null;
configDir = null;
libsDir = null;
jarDir = null;
nativesDir = null;
resourceDir = null;
cleanup();
}
public File getMinecraftDir() {
if (mcDir == null) mcDir = new File(DirectoryLocations.INSTANCE_DIR.format(getPathFriendlyName() + "/"));
return mcDir;
}
public File getModsDir() {
if (modsDir == null) modsDir = new File(getMinecraftDir(), "mods");
return modsDir;
}
public File getConfigDir() {
if (configDir == null) configDir = new File(getMinecraftDir(), "config");
return configDir;
}
public File getLibraryDir() {
if (libsDir == null) libsDir = new File(getMinecraftDir(), "libraries");
return libsDir;
}
public File getJarModsDir() {
if (jarDir == null) jarDir = new File(getMinecraftDir(), "jarMods");
return jarDir;
}
public File getNativesDir() {
if (nativesDir == null) nativesDir = new File(getLibraryDir(), "natives");
return nativesDir;
}
public File getResourceDir() {
if (resourceDir == null) resourceDir = new File(getMinecraftDir(), "resourcepacks");
return resourceDir;
}
private int numLoadedValidate = 0;
private int totalLaunchSize = 0;
private List<FileDownloader> downloads = new ArrayList<FileDownloader>();
private int currentLaunchSize = 0;
private int lastDownloadProgress = 0;
private int numLoadedDownloads = 0;
public void addValidatedFile(ProgressPanel panel, String filename, int fileSize, boolean shouldExtract) {
numLoadedValidate++;
panel.stateChanged("Validating mods", "[" + numLoadedValidate + "/" + downloads.size() + "]", (int) (((double) numLoadedValidate / (double) (downloads.size())) * 100D));
totalLaunchSize += ((double) fileSize * (shouldExtract ? 2D : 1D));
}
public boolean hasOptionalMod(String name) {
for (String s : getToken().modpackOptionalMods) {
if (s.equalsIgnoreCase(name)) { return true; }
}
return false;
}
public void addToOrder(String name) {
if (!jarOrder.isEmpty()) jarOrder += ",";
jarOrder += name;
}
public void prepDownload(ProgressPanel panel) {
mkdirs();
downloads.clear();
if (loader.isWholeDownload()) {
downloads.add(new FileDownloader(this, getDownloadLocation(), getMinecraftDir().getAbsolutePath(), true, false));
}
if (!modList.isEmpty()) {
for (Mod m : modList.values()) {
if (m.getDownloadUrl() == null || m.getDownloadUrl().isEmpty()) {
continue;
}
switch (m.getType()) {
default:
case optionalMod:
case mod:
downloads.add(new FileDownloader(this, m.getDownloadUrl(), getModsDir().getAbsolutePath(), m.getDownloadUrl().endsWith(".zip"), false));
break;
case config:
downloads.add(new FileDownloader(this, m.getDownloadUrl(), getConfigDir().getAbsolutePath(), true, false));
break;
case resourcePack:
downloads.add(new FileDownloader(this, m.getDownloadUrl(), getResourceDir().getAbsolutePath(), false, false));
break;
case library:
downloads.add(new FileDownloader(this, m.getDownloadUrl(), getLibraryDir().getAbsolutePath(), false, true));
break;
case global:
downloads.add(new FileDownloader(this, m.getDownloadUrl(), getMinecraftDir().getAbsolutePath(), true, false));
break;
case minecraft:
downloads.add(new FileDownloader(this, m.getDownloadUrl(), getLibraryDir().getAbsolutePath(), "minecraft.jar", true));
break;
case forge:
downloads.add(new FileDownloader(this, m.getDownloadUrl(), getJarModsDir().getAbsolutePath(), false, true));
break;
case natives:
downloads.add(new FileDownloader(this, m.getDownloadUrl(), getNativesDir().getAbsolutePath(), true, false));
break;
case resource:
downloads.add(new FileDownloader(this, m.getDownloadUrl(), m.getInfoUrl(), m.getName(), false));
break;
case assets:
downloads.add(new FileDownloader(this, m.getDownloadUrl(), m.getInfoUrl(), m.getName(), false));
break;
}
}
}
if (JOptionPane.showConfirmDialog(null, "Would you like us to install Optifine for you with this modpack?") == JOptionPane.OK_OPTION) {
downloads.add(new FileDownloader(this, "http://indigo.forsaken.co/downloads/OptiFine_1.7.10_HD_U_B4.jar", getModsDir().getAbsolutePath(), "OptiFine_1.7.10_HD_A4.jar", false));
}
if (JOptionPane.showConfirmDialog(null, "Would you like us to install FastCraft for you with this modpack?") == JOptionPane.OK_OPTION) {
downloads.add(new FileDownloader(this, "http://files.player.to/fastcraft-1.16.jar", getModsDir().getAbsolutePath(), false));
}
downloads.add(new FileDownloader(this, "http://indigo.forsaken.co/downloads/MinecraftLoader%200.1.3%20mc1.7.10.jar", getModsDir().getAbsolutePath(), false));
}
public boolean isFinishedDownloading() {
return downloads.size() <= numLoadedDownloads;
}
public void addLoadedDownload() {
numLoadedDownloads++;
}
public boolean download(final ProgressPanel panel, final boolean autoLogin) throws IOException {
new Thread() {
public void run() {
long downloadStartTime = System.currentTimeMillis();
prepDownload(panel);
ExecutorService pool = Executors.newFixedThreadPool(8);
for (FileDownloader res : downloads) {
if (res.shouldDownload()) {
pool.submit(res.loadFileSize(Server.this, panel));
}
}
pool.shutdown();
try {
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
} catch (InterruptedException e2) {
e2.printStackTrace();
}
pool = Executors.newFixedThreadPool(8);
try {
for (Runnable r : downloadFiles(panel)) {
pool.submit(r);
}
} catch (IOException e1) {
e1.printStackTrace();
}
pool.shutdown();
try {
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
pool = Executors.newFixedThreadPool(8);
if (loader instanceof TechnicServerLoader) {
File binDir = new File(getMinecraftDir(), "bin");
File modpackFile = new File(binDir, "modpack.jar");
try {
co.forsaken.projectindigo.utils.FileUtils.unzip(modpackFile, binDir);
FileUtils.copyFile(new File(binDir, "version.json"), new File(getMinecraftDir(), "version.json"));
FileUtils.deleteDirectory(binDir);
} catch (IOException e) {
e.printStackTrace();
}
}
if (loader instanceof FtbServerLoader || loader instanceof TechnicServerLoader) {
try {
String packName = "pack.json";
if (loader instanceof TechnicServerLoader) {
packName = "version.json";
}
PackToken token = (PackToken) (new Gson()).fromJson(FileUtils.readFileToString(new File(getMinecraftDir(), packName)), PackToken.class);
Artifact a;
for (Library lib : token.libraries) {
a = new Artifact(lib.name);
String dir = getLibraryDir().getAbsolutePath();
if (lib.name.toLowerCase().contains("minecraftforge")) {
dir = getJarModsDir().getAbsolutePath();
if (!lib.name.substring(lib.name.lastIndexOf(":") + 1).equals("universal")) {
lib.name += ":universal";
a = new Artifact(lib.name);
}
}
if (lib.url != null) {
pool.submit(new FileDownloader(Server.this, lib.url + a.getDownloadUrl(), dir, false).download(Server.this, panel));
} else {
pool.submit(new FileDownloader(Server.this, ServerLoader.MOJANG_LIBS_BASE + a.getDownloadUrl(), dir, false).download(Server.this, panel));
}
}
} catch (JsonSyntaxException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
pool.shutdown();
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
}
try {
// new File(getMinecraftDir(), "servers.dat").delete();
getLockFile().createNewFile();
co.forsaken.projectindigo.utils.FileUtils.writeStringToFile(getToken().version, getLockFile());
} catch (IOException e) {
e.printStackTrace();
}
panel.getMainPanel().switchPage(1);
launch(panel.getMainPanel(), autoLogin);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
return true;
}
private List<Runnable> downloadFiles(ProgressPanel panel) throws IOException {
List<Runnable> mods = new ArrayList<Runnable>();
for (FileDownloader download : downloads) {
if (download.shouldDownload()) {
mods.add(download.download(this, panel));
}
}
return mods;
}
private int parseVersion(String version) {
Pattern pat = Pattern.compile("\\.");
Matcher match = pat.matcher(version);
if (match.find()) {
version = match.replaceAll("");
}
return Integer.parseInt(version);
}
public void cleanupLibs() {
List<File> filesToRemove = new ArrayList<File>();
File[] files = getLibraryDir().listFiles();
for (File f : files) {
if (f.isDirectory()) continue;
for (File f2 : getLibraryDir().listFiles()) {
if (f2.isDirectory()) continue;
if (f.getName().equalsIgnoreCase("minecraft.jar") || f2.getName().equalsIgnoreCase("minecraft.jar") || f.getName().equals(f2.getName())) continue;
if (f.getName().substring(0, f.getName().lastIndexOf('-')).equalsIgnoreCase(f2.getName().substring(0, f2.getName().lastIndexOf('-')))) {
if (parseVersion(f2.getName().substring(f2.getName().lastIndexOf('-') + 1, f2.getName().lastIndexOf("."))) < parseVersion(f.getName().substring(f.getName().lastIndexOf('-') + 1, f.getName().lastIndexOf(".")))) {
filesToRemove.add(f2);
break;
}
}
}
}
for (File f : filesToRemove) {
f.delete();
}
}
public void writeServerFile() throws FileNotFoundException, IOException {
ArrayList<NbtServerToken> tokens = new ArrayList<NbtServerToken>();
tokens.add(new NbtServerToken("tFn - Lobby", "mc.forsaken.co"));
tokens.add(new NbtServerToken("tFn - " + getToken().friendlyName + " v" + getToken().version, getToken().friendlyIp));
NbtUtils.writeServersToFile(tokens, new File(getMinecraftDir(), "servers.dat"));
}
public void launch(final MainPanel panel, final boolean autoLogin) {
Thread launcher = new Thread() {
public void run() {
try {
writeServerFile();
} catch (FileNotFoundException e2) {
e2.printStackTrace();
} catch (IOException e2) {
e2.printStackTrace();
}
cleanupLibs();
try {
LogManager.info("Launching pack " + getToken().friendlyName + " " + getToken().version + " for " + "Minecraft 1.7.10");
IndigoLauncher._launcher.setVisible(false);
Process process = null;
try {
process = MinecraftLauncher.launchMinecraft(Server.this, ((LoginPanel) panel.getPanel(0)).getLoginResponse(), ((SettingsPanel) panel.getPanel(3)).getSettings(), autoLogin);
} catch (IOException ex) {
ex.printStackTrace();
}
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
LogManager.minecraft(line);
}
long end = System.currentTimeMillis();
int exitValue = 0; // Assume we exited fine
try {
exitValue = process.exitValue(); // Try to get the real exit value
} catch (IllegalThreadStateException e) {
e.printStackTrace();
process.destroy(); // Kill the process
}
if (exitValue != 0) {
if (JOptionPane.showConfirmDialog(null, "Your game has crashed for some reason, please upload a copy of your log...\n Would you like to open an issue now?") == JOptionPane.OK_OPTION) {
if (Desktop.isDesktopSupported()) {
try {
Desktop.getDesktop().browse(new URL("https://github.com/ForsakenDev/ProjectIndigo/issues/new").toURI());
} catch (IOException e1) {
e1.printStackTrace();
} catch (URISyntaxException e1) {
e1.printStackTrace();
}
}
}
}
((ServerBasePanel) panel.getPanel(1)).loadServers();
IndigoLauncher._launcher.setVisible(true);
} catch (IOException e1) {
e1.printStackTrace();
}
}
};
launcher.start();
}
public int getDownloadProgress() {
int prog = (int) ((double) ((double) currentLaunchSize / (double) totalLaunchSize) * 100);
if (prog > 100) {
prog = 100;
} else if (prog < 0) {
prog = 0;
}
return prog;
}
DecimalFormat formatter = new DecimalFormat("### ###.00");
List<String> values = new ArrayList<String>();
long lastUpdate = System.currentTimeMillis();
public void addDownloadSize(ProgressPanel panel, String type, String fileName, int amount) {
currentLaunchSize += amount;
if (totalLaunchSize < currentLaunchSize) totalLaunchSize = currentLaunchSize;
lastDownloadProgress = getDownloadProgress();
panel.stateChanged("Downloading mods", formatter.format((double) currentLaunchSize / (1024D * 1024D)) + "Mb/" + formatter.format((double) totalLaunchSize / (1024D * 1024D)) + "Mb", lastDownloadProgress);
}
}