Compare commits
11 Commits
v1.3.5-bet
...
v1.4.0-bet
Author | SHA1 | Date | |
---|---|---|---|
a1ae162b07 | |||
b2ee22eb7b | |||
65ede11ab5 | |||
c26a3bc3b5 | |||
71e47acbb0 | |||
60fdcf5ddc | |||
bf77c13072 | |||
849655bfc6 | |||
e8ecee1cd0 | |||
5e24d5daa8 | |||
9532683301 |
13
README.md
13
README.md
@ -1,6 +1,13 @@
|
|||||||
# Minecraft-Server-Launcher
|
# Minecraft-Server-Launcher
|
||||||
I originally created this software in 2013 using AutoIt. Since I am now learning Java, I have recreated it in Java.
|
I originally created this software in 2013 using AutoIt. After learning Java, I recreated it in Java.
|
||||||
The original version can be found at: https://archive.knarcraft.net/Scripts/BungeeMinecraftServerLauncher/
|
The original version can be found at: https://archive.knarcraft.net/Scripts/BungeeMinecraftServerLauncher/
|
||||||
Its goal is to do everything the original does, just better. The original is 1595 lines of code repetition and dirty code. I had no prior programming experience when I first started working on the original, which is why it became such a mess.
|
This version's goal is to do everything the original does, just better. The original is 1595 lines of code repetition
|
||||||
|
and dirty code. I had no prior programming experience when I first started working on the original, which is why it
|
||||||
|
became such a mess.
|
||||||
|
|
||||||
The code is now in a fully working state. It is still missing an auto update feature, but it can be used, and is fully functional.
|
The software is in a fully working stage with a functional updater. The software is still in beta because there are still
|
||||||
|
a lot of things I want to change and small annoyances. The software is fully portable. Make sure to put it in its own
|
||||||
|
folder as it will create lots of files.
|
||||||
|
|
||||||
|
The updater will move the new version to minecraft-server-launcher.jar, so you should rename the downloaded
|
||||||
|
file to this if it's named something else to avoid confusion.
|
@ -11,6 +11,7 @@ import javax.naming.ConfigurationException;
|
|||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keeps track of a set of servers and some user settings
|
* Keeps track of a set of servers and some user settings
|
||||||
@ -22,7 +23,7 @@ import java.util.ArrayList;
|
|||||||
public class Profile {
|
public class Profile {
|
||||||
|
|
||||||
private static final ServerLauncherGUI serverLauncherGui = Main.getController().getGUI();
|
private static final ServerLauncherGUI serverLauncherGui = Main.getController().getGUI();
|
||||||
private final ArrayList<Collection> collections;
|
private final List<Collection> collections;
|
||||||
private final String name;
|
private final String name;
|
||||||
private boolean runInBackground;
|
private boolean runInBackground;
|
||||||
private int delayStartup;
|
private int delayStartup;
|
||||||
@ -106,7 +107,7 @@ public class Profile {
|
|||||||
*
|
*
|
||||||
* @return <p>All collections stored by this profile</p>
|
* @return <p>All collections stored by this profile</p>
|
||||||
*/
|
*/
|
||||||
public ArrayList<Collection> getCollections() {
|
public List<Collection> getCollections() {
|
||||||
return this.collections;
|
return this.collections;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,9 +326,10 @@ public class Server {
|
|||||||
/**
|
/**
|
||||||
* Runs a Minecraft server
|
* Runs a Minecraft server
|
||||||
*
|
*
|
||||||
|
* @param skipDelay <p>Whether to skip the startup delay for this server</p>
|
||||||
* @return <p>True if nothing went wrong</p>
|
* @return <p>True if nothing went wrong</p>
|
||||||
*/
|
*/
|
||||||
public boolean runServer(boolean isFirstServer) {
|
public boolean runServer(boolean skipDelay) {
|
||||||
if (ServerHandler.stoppingServers()) {
|
if (ServerHandler.stoppingServers()) {
|
||||||
gui.logMessage("Stopping servers. Cannot start yet.");
|
gui.logMessage("Stopping servers. Cannot start yet.");
|
||||||
return false;
|
return false;
|
||||||
@ -339,12 +340,13 @@ public class Server {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//Tries to do necessary pre-start work
|
//Tries to do necessary pre-start work
|
||||||
if (!initializeJarDownload() || (!isFirstServer && !delayStartup())) {
|
if (!initializeJarDownload() || (!skipDelay && !delayStartup())) {
|
||||||
gui.logError("Failed to perform startup tasks.");
|
gui.logError("Failed to perform startup tasks.");
|
||||||
this.started = false;
|
this.started = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (ServerHandler.stoppingServers()) {
|
if (ServerHandler.stoppingServers()) {
|
||||||
|
gui.logMessage("Stopping servers. Cannot start yet.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//Starts the server if possible
|
//Starts the server if possible
|
||||||
@ -367,14 +369,17 @@ public class Server {
|
|||||||
*/
|
*/
|
||||||
private String getJavaCommand() {
|
private String getJavaCommand() {
|
||||||
ServerLauncherController controller = ServerLauncherController.getInstance();
|
ServerLauncherController controller = ServerLauncherController.getInstance();
|
||||||
|
|
||||||
if (serverVersion.toLowerCase().contains("latest")) {
|
if (serverVersion.toLowerCase().contains("latest")) {
|
||||||
return controller.getJavaCommand();
|
return controller.getJavaCommand();
|
||||||
} else if (serverVersion.contains(".") && serverVersion.split("\\.").length >= 2 &&
|
} else if (serverVersion.contains(".") && serverVersion.split("\\.").length >= 2) {
|
||||||
Integer.parseInt(serverVersion.split("\\.")[1]) >= 17) {
|
try {
|
||||||
return controller.getJavaCommand();
|
if (Integer.parseInt(serverVersion.split("\\.")[1]) >= 17) {
|
||||||
} else {
|
return controller.getJavaCommand();
|
||||||
return controller.getOldJavaCommand();
|
}
|
||||||
|
} catch (NumberFormatException ignored) {}
|
||||||
}
|
}
|
||||||
|
return controller.getOldJavaCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -117,9 +117,11 @@ public class ServerHandler {
|
|||||||
public static void startServers() {
|
public static void startServers() {
|
||||||
ServerLauncherController controller = Main.getController();
|
ServerLauncherController controller = Main.getController();
|
||||||
gui.setStatus("Starting servers");
|
gui.setStatus("Starting servers");
|
||||||
int serverNum = 0;
|
Server previouslyStartedServer = null;
|
||||||
|
|
||||||
for (Collection collection : controller.getCurrentProfile().getCollections()) {
|
for (Collection collection : controller.getCurrentProfile().getCollections()) {
|
||||||
if (!collection.getServer().runServer(serverNum++ == 0)) {
|
Server server = collection.getServer();
|
||||||
|
if (!server.runServer(previouslyStartedServer == null || previouslyStartedServer.isProxy())) {
|
||||||
gui.showError("An error occurred. Start aborted. Please check relevant log files.");
|
gui.showError("An error occurred. Start aborted. Please check relevant log files.");
|
||||||
try {
|
try {
|
||||||
stop();
|
stop();
|
||||||
@ -129,6 +131,9 @@ public class ServerHandler {
|
|||||||
gui.updateGUIElementsWhenServersStartOrStop(false);
|
gui.updateGUIElementsWhenServersStartOrStop(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (server.isEnabled()) {
|
||||||
|
previouslyStartedServer = server;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,95 @@
|
|||||||
|
package net.knarcraft.minecraftserverlauncher.userinterface;
|
||||||
|
|
||||||
|
import net.knarcraft.minecraftserverlauncher.utility.BackupUtil;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Backup GUI is used to display backup progress
|
||||||
|
*/
|
||||||
|
public class BackupGUI implements ActionListener {
|
||||||
|
private static JFrame frame;
|
||||||
|
private static JTextArea progressTextArea;
|
||||||
|
private static JProgressBar progressBar;
|
||||||
|
private static JButton cancelButton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new GUI
|
||||||
|
*/
|
||||||
|
public BackupGUI() {
|
||||||
|
instantiate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the server consoles frame
|
||||||
|
*/
|
||||||
|
public void instantiate() {
|
||||||
|
if (frame != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
frame = new JFrame("Running backup...");
|
||||||
|
frame.setBounds(100, 100, 500, 140);
|
||||||
|
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
||||||
|
progressTextArea = new JTextArea();
|
||||||
|
progressTextArea.setEditable(false);
|
||||||
|
progressBar = new JProgressBar();
|
||||||
|
cancelButton = new JButton("Cancel");
|
||||||
|
JPanel panel = new JPanel();
|
||||||
|
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
|
||||||
|
panel.add(progressTextArea);
|
||||||
|
panel.add(Box.createRigidArea(new Dimension(0,5)));
|
||||||
|
panel.add(progressBar);
|
||||||
|
|
||||||
|
JPanel buttonPane = new JPanel();
|
||||||
|
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
|
||||||
|
buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
|
||||||
|
buttonPane.add(Box.createHorizontalGlue());
|
||||||
|
buttonPane.add(cancelButton);
|
||||||
|
cancelButton.addActionListener(BackupGUI.this);
|
||||||
|
|
||||||
|
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
|
Container contentPane = frame.getContentPane();
|
||||||
|
contentPane.add(panel, BorderLayout.CENTER);
|
||||||
|
contentPane.add(buttonPane, BorderLayout.PAGE_END);
|
||||||
|
frame.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates information about the backup progress
|
||||||
|
*
|
||||||
|
* @param infoText <p>The text to display</p>
|
||||||
|
* @param progressPercent <p>The new percent of the progress bar</p>
|
||||||
|
*/
|
||||||
|
public static void updateProgress(String infoText, int progressPercent) {
|
||||||
|
if (progressTextArea != null) {
|
||||||
|
progressTextArea.setText(infoText);
|
||||||
|
}
|
||||||
|
if (progressBar != null) {
|
||||||
|
progressBar.setValue(progressPercent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the backup GUI
|
||||||
|
*/
|
||||||
|
public static void destroy() {
|
||||||
|
if (frame != null) {
|
||||||
|
frame.dispose();
|
||||||
|
frame = null;
|
||||||
|
progressBar = null;
|
||||||
|
progressTextArea = null;
|
||||||
|
cancelButton = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
|
if (actionEvent.getSource() == cancelButton) {
|
||||||
|
BackupUtil.abortBackup();
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -93,7 +93,7 @@ public class Console extends KeyAdapter implements ActionListener, KeyListener {
|
|||||||
StringBuilder newTextBuilder = new StringBuilder();
|
StringBuilder newTextBuilder = new StringBuilder();
|
||||||
for (String line : oldTextList) {
|
for (String line : oldTextList) {
|
||||||
if (!line.equals("")) {
|
if (!line.equals("")) {
|
||||||
newTextBuilder.append(line);
|
newTextBuilder.append(line).append("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.textOutput.setText(newTextBuilder.toString());
|
this.textOutput.setText(newTextBuilder.toString());
|
||||||
|
@ -4,7 +4,7 @@ import net.knarcraft.minecraftserverlauncher.Main;
|
|||||||
import net.knarcraft.minecraftserverlauncher.profile.Collection;
|
import net.knarcraft.minecraftserverlauncher.profile.Collection;
|
||||||
import net.knarcraft.minecraftserverlauncher.profile.ServerLauncherController;
|
import net.knarcraft.minecraftserverlauncher.profile.ServerLauncherController;
|
||||||
import net.knarcraft.minecraftserverlauncher.server.ServerHandler;
|
import net.knarcraft.minecraftserverlauncher.server.ServerHandler;
|
||||||
import net.knarcraft.minecraftserverlauncher.utility.CommonFunctions;
|
import net.knarcraft.minecraftserverlauncher.utility.BackupUtil;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.naming.ConfigurationException;
|
import javax.naming.ConfigurationException;
|
||||||
@ -33,7 +33,7 @@ import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.getR
|
|||||||
*/
|
*/
|
||||||
public class ServerLauncherGUI extends MessageHandler implements ActionListener, GUI {
|
public class ServerLauncherGUI extends MessageHandler implements ActionListener, GUI {
|
||||||
|
|
||||||
private final JLabel lblStatuslabel = new JLabel("Servers are stopped");
|
private final JLabel lblStatusLabel = new JLabel("Servers are stopped");
|
||||||
private final ServerLauncherController controller;
|
private final ServerLauncherController controller;
|
||||||
private Map<String, String> textStrings;
|
private Map<String, String> textStrings;
|
||||||
private Tray applicationTray;
|
private Tray applicationTray;
|
||||||
@ -101,7 +101,7 @@ public class ServerLauncherGUI extends MessageHandler implements ActionListener,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setStatus(String text) {
|
public void setStatus(String text) {
|
||||||
this.lblStatuslabel.setText(text);
|
this.lblStatusLabel.setText(text);
|
||||||
this.logMessage(text);
|
this.logMessage(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,11 +243,11 @@ public class ServerLauncherGUI extends MessageHandler implements ActionListener,
|
|||||||
panelBasic.add(profiles);
|
panelBasic.add(profiles);
|
||||||
profiles.addActionListener(this);
|
profiles.addActionListener(this);
|
||||||
|
|
||||||
sl_panel.putConstraint(SpringLayout.NORTH, lblStatuslabel, 6, SpringLayout.SOUTH, addProfileButton);
|
sl_panel.putConstraint(SpringLayout.NORTH, lblStatusLabel, 6, SpringLayout.SOUTH, addProfileButton);
|
||||||
sl_panel.putConstraint(SpringLayout.SOUTH, lblStatuslabel, -10, SpringLayout.SOUTH, panelBasic);
|
sl_panel.putConstraint(SpringLayout.SOUTH, lblStatusLabel, -10, SpringLayout.SOUTH, panelBasic);
|
||||||
sl_panel.putConstraint(SpringLayout.WEST, lblStatuslabel, 10, SpringLayout.WEST, panelBasic);
|
sl_panel.putConstraint(SpringLayout.WEST, lblStatusLabel, 10, SpringLayout.WEST, panelBasic);
|
||||||
sl_panel.putConstraint(SpringLayout.EAST, lblStatuslabel, -10, SpringLayout.EAST, panelBasic);
|
sl_panel.putConstraint(SpringLayout.EAST, lblStatusLabel, -10, SpringLayout.EAST, panelBasic);
|
||||||
panelBasic.add(lblStatuslabel);
|
panelBasic.add(lblStatusLabel);
|
||||||
|
|
||||||
addServerButton = new JButton("Add server");
|
addServerButton = new JButton("Add server");
|
||||||
sl_panel.putConstraint(SpringLayout.NORTH, addServerButton, 0, SpringLayout.NORTH, startServerButton);
|
sl_panel.putConstraint(SpringLayout.NORTH, addServerButton, 0, SpringLayout.NORTH, startServerButton);
|
||||||
@ -314,7 +314,7 @@ public class ServerLauncherGUI extends MessageHandler implements ActionListener,
|
|||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
Object actionSource = e.getSource();
|
Object actionSource = e.getSource();
|
||||||
//Registers actions on the main tab
|
//Register actions on the main tab
|
||||||
handleMainTabButtons(actionSource);
|
handleMainTabButtons(actionSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,7 +333,8 @@ public class ServerLauncherGUI extends MessageHandler implements ActionListener,
|
|||||||
} else if (actionSource == addServerButton) {
|
} else if (actionSource == addServerButton) {
|
||||||
addServer();
|
addServer();
|
||||||
} else if (actionSource == backupButton) {
|
} else if (actionSource == backupButton) {
|
||||||
CommonFunctions.backup(this);
|
//Run backup in its own thread to prevent locking up
|
||||||
|
Executors.newSingleThreadExecutor().execute(() -> BackupUtil.backup(this));
|
||||||
} else if (actionSource == addProfileButton) {
|
} else if (actionSource == addProfileButton) {
|
||||||
controller.addProfile(JOptionPane.showInputDialog("Profile name: "));
|
controller.addProfile(JOptionPane.showInputDialog("Profile name: "));
|
||||||
updateProfiles();
|
updateProfiles();
|
||||||
|
@ -0,0 +1,264 @@
|
|||||||
|
package net.knarcraft.minecraftserverlauncher.utility;
|
||||||
|
|
||||||
|
import net.knarcraft.minecraftserverlauncher.Main;
|
||||||
|
import net.knarcraft.minecraftserverlauncher.profile.Collection;
|
||||||
|
import net.knarcraft.minecraftserverlauncher.server.Server;
|
||||||
|
import net.knarcraft.minecraftserverlauncher.userinterface.BackupGUI;
|
||||||
|
import net.knarcraft.minecraftserverlauncher.userinterface.GUI;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper class for performing server backup
|
||||||
|
*/
|
||||||
|
public class BackupUtil {
|
||||||
|
|
||||||
|
private static boolean backupAborted;
|
||||||
|
private static boolean backupRunning = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aborts the currently running backup
|
||||||
|
*/
|
||||||
|
public static void abortBackup() {
|
||||||
|
backupAborted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively copies a folder to another location
|
||||||
|
*
|
||||||
|
* @param source <p>The folder to copy</p>
|
||||||
|
* @param destination <p>Target destination</p>
|
||||||
|
* @param backupFileSize <p>The total file size of the backup in progress</p>
|
||||||
|
* @param alreadyCopied <p>The amount of bytes already copied</p>
|
||||||
|
* @throws IOException <p>If we can't start a file stream</p>
|
||||||
|
*/
|
||||||
|
private static long backupFolder(File source, File destination, long backupFileSize,
|
||||||
|
long alreadyCopied) throws IOException {
|
||||||
|
if (backupAborted) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
if (!source.isDirectory()) {
|
||||||
|
long copiedFileSize = copyFile(source, destination);
|
||||||
|
BackupGUI.updateProgress("Copying " + source + "\n to " + destination,
|
||||||
|
(int) ((alreadyCopied + copiedFileSize) * 100 / backupFileSize));
|
||||||
|
return copiedFileSize;
|
||||||
|
} else {
|
||||||
|
if (!destination.exists() && !destination.mkdir()) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
String[] files = source.list();
|
||||||
|
long copiedFilesSize = 0;
|
||||||
|
if (files != null) {
|
||||||
|
for (String file : files) {
|
||||||
|
File srcFile = new File(source, file);
|
||||||
|
File destinationFile = new File(destination, file);
|
||||||
|
copiedFilesSize += backupFolder(srcFile, destinationFile, backupFileSize,
|
||||||
|
alreadyCopied + copiedFilesSize);
|
||||||
|
BackupGUI.updateProgress("Copying " + source + "\n to " + destination,
|
||||||
|
(int) ((alreadyCopied + copiedFilesSize) * 100 / backupFileSize));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return copiedFilesSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies a file from one location to another
|
||||||
|
*
|
||||||
|
* @param source <p>The file to copy</p>
|
||||||
|
* @param destination <p>The location of the copied file</p>
|
||||||
|
* @throws IOException <p>If reading or writing fails</p>
|
||||||
|
*/
|
||||||
|
private static long copyFile(File source, File destination) throws IOException {
|
||||||
|
InputStream in = new FileInputStream(source);
|
||||||
|
OutputStream out = new FileOutputStream(destination);
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int length;
|
||||||
|
while ((length = in.read(buffer)) > 0) {
|
||||||
|
out.write(buffer, 0, length);
|
||||||
|
}
|
||||||
|
in.close();
|
||||||
|
out.close();
|
||||||
|
return Files.size(source.toPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies all server directories to a folder specified by the user
|
||||||
|
*
|
||||||
|
* @param gui <p>The GUI to use for informing the user</p>
|
||||||
|
*/
|
||||||
|
public static void backup(GUI gui) {
|
||||||
|
backupAborted = false;
|
||||||
|
if (backupRunning) {
|
||||||
|
gui.setStatus("A backup is already running");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
backupRunning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get the folder to save the backed up files in
|
||||||
|
File path = gui.askForDirectory("Backup folder");
|
||||||
|
if (path == null || !path.isDirectory()) {
|
||||||
|
backupRunning = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gui.setStatus("Backup running...");
|
||||||
|
List<List<File>> serverFolders = getFoldersOfEnabledServers(path);
|
||||||
|
|
||||||
|
gui.setStatus("Calculating backup size...");
|
||||||
|
long backupFileSize = getFolderSize(gui, serverFolders);
|
||||||
|
|
||||||
|
long locationFreeSpace = path.getFreeSpace();
|
||||||
|
if (locationFreeSpace < (backupFileSize + 2048000000)) {
|
||||||
|
gui.setStatus("Not enough available space. " + (backupFileSize / 1000000) + "MB necessary, but only " +
|
||||||
|
(locationFreeSpace / 1000000) + "MB available");
|
||||||
|
backupRunning = false;
|
||||||
|
backupAborted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gui.setStatus("Backing up " + (backupFileSize / 1000000) + "MB");
|
||||||
|
|
||||||
|
performBackup(gui, serverFolders, backupFileSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the actual backup after checks have passed and necessary info is available
|
||||||
|
* @param gui <p>The GUI to use for informing the user</p>
|
||||||
|
* @param serverFolders <p>The folders of the servers to backup</p>
|
||||||
|
* @param backupFileSize <p>The total size of the folders to backup</p>
|
||||||
|
*/
|
||||||
|
private static void performBackup(GUI gui, List<List<File>> serverFolders, long backupFileSize) {
|
||||||
|
new BackupGUI();
|
||||||
|
BackupGUI.updateProgress("Backup starting...", 0);
|
||||||
|
long alreadyCopied = 0;
|
||||||
|
for (List<File> serverFolder : serverFolders) {
|
||||||
|
if (backupAborted || !backupRunning) {
|
||||||
|
gui.setStatus("Backup aborted");
|
||||||
|
backupRunning = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
alreadyCopied = backupServerFiles(gui, serverFolder, backupFileSize, alreadyCopied);
|
||||||
|
}
|
||||||
|
backupRunning = false;
|
||||||
|
if (backupAborted) {
|
||||||
|
gui.setStatus("Backup aborted");
|
||||||
|
} else {
|
||||||
|
BackupGUI.destroy();
|
||||||
|
gui.setStatus("Backup finished");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backs up the files for a single server
|
||||||
|
*
|
||||||
|
* @param gui <p>The GUI to use for informing the user</p>
|
||||||
|
* @param serverFolder <p>The server's input and output folders</p>
|
||||||
|
* @param backupFileSize <p>The total size of the files to backup</p>
|
||||||
|
* @param alreadyCopied <p>The amount of bytes already copied</p>
|
||||||
|
* @return <p>The new amount of bytes copied</p>
|
||||||
|
*/
|
||||||
|
private static long backupServerFiles(GUI gui, List<File> serverFolder, long backupFileSize, long alreadyCopied) {
|
||||||
|
File srcFolder = serverFolder.get(0);
|
||||||
|
File destinationFolder = serverFolder.get(1);
|
||||||
|
|
||||||
|
//Create child folders
|
||||||
|
if (!destinationFolder.exists() && !destinationFolder.mkdirs()) {
|
||||||
|
backupRunning = false;
|
||||||
|
gui.logError("Unable to create necessary sub-folder in the backup folder");
|
||||||
|
throw new IllegalArgumentException("Unable to create necessary sub-folder in the backup folder");
|
||||||
|
}
|
||||||
|
//Backup
|
||||||
|
try {
|
||||||
|
alreadyCopied += backupFolder(srcFolder, destinationFolder, backupFileSize, alreadyCopied);
|
||||||
|
} catch (IOException e) {
|
||||||
|
gui.showError("Backup caused an error: " + e.getMessage());
|
||||||
|
gui.logError(Arrays.toString(e.getStackTrace()));
|
||||||
|
BackupGUI.destroy();
|
||||||
|
backupRunning = false;
|
||||||
|
}
|
||||||
|
return alreadyCopied;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the size of a list of folders
|
||||||
|
*
|
||||||
|
* @param gui <p>The GUI to write any errors to</p>
|
||||||
|
* @param serverFolders <p>The folder to find the size of</p>
|
||||||
|
* @return <p>The size of the given folders</p>
|
||||||
|
*/
|
||||||
|
private static long getFolderSize(GUI gui, List<List<File>> serverFolders) {
|
||||||
|
long folderSize = 0;
|
||||||
|
for (List<File> serverFolder : serverFolders) {
|
||||||
|
File srcFolder = serverFolder.get(0);
|
||||||
|
try (Stream<Path> walk = Files.walk(srcFolder.toPath())) {
|
||||||
|
folderSize += walk.filter(Files::isRegularFile).mapToLong(BackupUtil::getFileSize).sum();
|
||||||
|
} catch (IOException e) {
|
||||||
|
gui.setStatus(String.format("IO errors %s", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return folderSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the input and output folders for enabled servers
|
||||||
|
*
|
||||||
|
* <p>The input folders are the folders to copy, while the output folders are the folders to write the backup to.
|
||||||
|
* Each list element contains a list of exactly two File items. The first one is the input folder, and the second
|
||||||
|
* one is the output folder</p>
|
||||||
|
*
|
||||||
|
* @param path <p>The path of the backup folder, as given by the user</p>
|
||||||
|
* @return <p>The folders to copy from/to</p>
|
||||||
|
*/
|
||||||
|
private static List<List<File>> getFoldersOfEnabledServers(File path) {
|
||||||
|
List<List<File>> serverFolders = new ArrayList<>();
|
||||||
|
|
||||||
|
//Get folders of servers to back up
|
||||||
|
List<Collection> collections = Main.getController().getCurrentProfile().getCollections();
|
||||||
|
for (Collection collection : collections) {
|
||||||
|
//Ignore disabled and invalid servers
|
||||||
|
if (collection.getServer().getPath().equals("") || !collection.getServer().isEnabled()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//Decide sub-folders
|
||||||
|
Server targetServer = collection.getServer();
|
||||||
|
String name = targetServer.getName();
|
||||||
|
File srcFolder = new File(targetServer.getPath());
|
||||||
|
File destinationFolder = new File(path, name);
|
||||||
|
|
||||||
|
List<File> serverFolder = new ArrayList<>();
|
||||||
|
serverFolder.add(srcFolder);
|
||||||
|
serverFolder.add(destinationFolder);
|
||||||
|
serverFolders.add(serverFolder);
|
||||||
|
}
|
||||||
|
return serverFolders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the size of a file given its path
|
||||||
|
*
|
||||||
|
* @param path <p>The path to a file</p>
|
||||||
|
* @return <p>The size of the file in bytes, or 0 if an exception is thrown</p>
|
||||||
|
*/
|
||||||
|
private static long getFileSize(Path path) {
|
||||||
|
try {
|
||||||
|
return Files.size(path);
|
||||||
|
} catch (IOException exception) {
|
||||||
|
System.out.printf("Failed to get size of %s%n%s", path, exception);
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,9 +1,6 @@
|
|||||||
package net.knarcraft.minecraftserverlauncher.utility;
|
package net.knarcraft.minecraftserverlauncher.utility;
|
||||||
|
|
||||||
import net.knarcraft.minecraftserverlauncher.Main;
|
import net.knarcraft.minecraftserverlauncher.Main;
|
||||||
import net.knarcraft.minecraftserverlauncher.profile.Collection;
|
|
||||||
import net.knarcraft.minecraftserverlauncher.server.Server;
|
|
||||||
import net.knarcraft.minecraftserverlauncher.userinterface.GUI;
|
|
||||||
import net.knarcraft.minecraftserverlauncher.userinterface.WebBrowser;
|
import net.knarcraft.minecraftserverlauncher.userinterface.WebBrowser;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
@ -16,7 +13,6 @@ import java.io.FileWriter;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
@ -53,7 +49,7 @@ public final class CommonFunctions {
|
|||||||
* @param folder <p>The folder to create</p>
|
* @param folder <p>The folder to create</p>
|
||||||
* @throws FileNotFoundException <p>If unable to create the folder</p>
|
* @throws FileNotFoundException <p>If unable to create the folder</p>
|
||||||
*/
|
*/
|
||||||
protected static void createFolder(File folder) throws FileNotFoundException {
|
public static void createFolder(File folder) throws FileNotFoundException {
|
||||||
if (!folder.exists()) {
|
if (!folder.exists()) {
|
||||||
if (!folder.mkdirs()) {
|
if (!folder.mkdirs()) {
|
||||||
throw new FileNotFoundException("Cannot create necessary directory.");
|
throw new FileNotFoundException("Cannot create necessary directory.");
|
||||||
@ -210,55 +206,6 @@ public final class CommonFunctions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively copies a folder to another location
|
|
||||||
*
|
|
||||||
* @param source <p>The folder to copy</p>
|
|
||||||
* @param destination <p>Target destination</p>
|
|
||||||
* @throws IOException <p>If we can't start a file stream</p>
|
|
||||||
*/
|
|
||||||
private static void copyFolder(GUI serverLauncherGui, File source, File destination) throws IOException {
|
|
||||||
if (!source.isDirectory()) {
|
|
||||||
copyFile(serverLauncherGui, source, destination);
|
|
||||||
} else {
|
|
||||||
serverLauncherGui.setStatus("Copying directory " + source);
|
|
||||||
if (!destination.exists() && !destination.mkdir()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String[] files = source.list();
|
|
||||||
if (files != null) {
|
|
||||||
for (String file : files) {
|
|
||||||
File srcFile = new File(source, file);
|
|
||||||
File destinationFile = new File(destination, file);
|
|
||||||
copyFolder(serverLauncherGui, srcFile, destinationFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
serverLauncherGui.setStatus("Copied directory " + source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies a file from one location to another
|
|
||||||
*
|
|
||||||
* @param serverLauncherGui <p>The serverLauncherGui to use for alerting the user</p>
|
|
||||||
* @param source <p>The file to copy</p>
|
|
||||||
* @param destination <p>The location of the copied file</p>
|
|
||||||
* @throws IOException <p>If reading or writing fails</p>
|
|
||||||
*/
|
|
||||||
private static void copyFile(GUI serverLauncherGui, File source, File destination) throws IOException {
|
|
||||||
serverLauncherGui.setStatus("Copying file " + source + "...");
|
|
||||||
InputStream in = new FileInputStream(source);
|
|
||||||
OutputStream out = new FileOutputStream(destination);
|
|
||||||
byte[] buffer = new byte[1024];
|
|
||||||
int length;
|
|
||||||
while ((length = in.read(buffer)) > 0) {
|
|
||||||
out.write(buffer, 0, length);
|
|
||||||
}
|
|
||||||
in.close();
|
|
||||||
out.close();
|
|
||||||
serverLauncherGui.setStatus("Copied file " + source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens an url in the user's default application.
|
* Opens an url in the user's default application.
|
||||||
*
|
*
|
||||||
@ -295,34 +242,7 @@ public final class CommonFunctions {
|
|||||||
return text.toString().trim();
|
return text.toString().trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies all server directories to a folder specified by the user.
|
|
||||||
*/
|
|
||||||
public static void backup(GUI gui) {
|
|
||||||
File path = gui.askForDirectory("Backup folder");
|
|
||||||
for (Collection collection : Main.getController().getCurrentProfile().getCollections()) {
|
|
||||||
//Ignore disabled and invalid servers
|
|
||||||
if (collection.getServer().getPath().equals("") || !collection.getServer().isEnabled()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
//Decide sub-folders
|
|
||||||
Server targetServer = collection.getServer();
|
|
||||||
String name = targetServer.getName();
|
|
||||||
File srcFolder = new File(targetServer.getPath());
|
|
||||||
File destinationFolder = new File(path, name);
|
|
||||||
//Create child folders
|
|
||||||
if (!destinationFolder.exists() && !destinationFolder.mkdirs()) {
|
|
||||||
throw new IllegalArgumentException("Unable to create necessary sub-folder in the backup folder");
|
|
||||||
}
|
|
||||||
//Backup
|
|
||||||
try {
|
|
||||||
CommonFunctions.copyFolder(gui, srcFolder, destinationFolder);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gui.setStatus("Backup finished");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates that a name is not empty and does not contain invalid characters
|
* Validates that a name is not empty and does not contain invalid characters
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
beta
|
beta
|
||||||
1.3.5
|
1.4.0
|
|
Reference in New Issue
Block a user