12 Commits

Author SHA1 Message Date
a1ae162b07 Updates software version to 1.4.0
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
2021-09-28 02:32:00 +02:00
b2ee22eb7b Disable synchronous behavior to prevent odd behavior when the backup button is spam clicked
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
2021-09-27 23:23:06 +02:00
65ede11ab5 Improves behavior when an error occurs during backup 2021-09-27 23:19:52 +02:00
c26a3bc3b5 Adds some backup fixes
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
Fixes a potential NullPointerException
Adds a check to make sure the backup location has enough available space
Adds a message when calculating backup size to make it clear that the software is not frozen
2021-09-27 22:24:06 +02:00
71e47acbb0 Improves the readability of the backup code a bit
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
2021-09-27 22:06:56 +02:00
60fdcf5ddc Adds a proper GUI to display backup progress
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
Extracts backup code to its own class
Adds a new GUI to display progress and the file copied
2021-09-27 21:39:15 +02:00
bf77c13072 Fixes some behavior regarding backups
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
Makes sure to only proceed with backups if a valid path is given
Makes the backup run in its own thread to prevent the software from locking up
Makes sure only one backup can be running at once
2021-09-27 18:06:33 +02:00
849655bfc6 Fixes a bug causing newlines to disappear when a console is truncated
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
2021-08-26 14:19:40 +02:00
e8ecee1cd0 Updates the README as it was starting to get quite outdated 2021-08-25 23:32:13 +02:00
5e24d5daa8 Improves checking for whether a server should be delayed before starting
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
2021-08-24 16:08:02 +02:00
9532683301 Fixes a NumberFormatException when running custom versions without a version number
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
2021-08-23 16:43:35 +02:00
365d08f2e2 Changes download URLs of Paper to papermc.io
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
Updates software version
Stores version of downloaded Paper .jar files
Completes #8
2021-08-23 14:53:39 +02:00
15 changed files with 451 additions and 119 deletions

View File

@ -1,6 +1,13 @@
# 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/
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.

View File

@ -11,6 +11,7 @@ import javax.naming.ConfigurationException;
import javax.swing.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Keeps track of a set of servers and some user settings
@ -22,7 +23,7 @@ import java.util.ArrayList;
public class Profile {
private static final ServerLauncherGUI serverLauncherGui = Main.getController().getGUI();
private final ArrayList<Collection> collections;
private final List<Collection> collections;
private final String name;
private boolean runInBackground;
private int delayStartup;
@ -106,7 +107,7 @@ public class Profile {
*
* @return <p>All collections stored by this profile</p>
*/
public ArrayList<Collection> getCollections() {
public List<Collection> getCollections() {
return this.collections;
}

View File

@ -326,9 +326,10 @@ public class 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>
*/
public boolean runServer(boolean isFirstServer) {
public boolean runServer(boolean skipDelay) {
if (ServerHandler.stoppingServers()) {
gui.logMessage("Stopping servers. Cannot start yet.");
return false;
@ -339,12 +340,13 @@ public class Server {
return true;
}
//Tries to do necessary pre-start work
if (!initializeJarDownload() || (!isFirstServer && !delayStartup())) {
if (!initializeJarDownload() || (!skipDelay && !delayStartup())) {
gui.logError("Failed to perform startup tasks.");
this.started = false;
return false;
}
if (ServerHandler.stoppingServers()) {
gui.logMessage("Stopping servers. Cannot start yet.");
return false;
}
//Starts the server if possible
@ -367,14 +369,17 @@ public class Server {
*/
private String getJavaCommand() {
ServerLauncherController controller = ServerLauncherController.getInstance();
if (serverVersion.toLowerCase().contains("latest")) {
return controller.getJavaCommand();
} else if (serverVersion.contains(".") && serverVersion.split("\\.").length >= 2 &&
Integer.parseInt(serverVersion.split("\\.")[1]) >= 17) {
return controller.getJavaCommand();
} else {
return controller.getOldJavaCommand();
} else if (serverVersion.contains(".") && serverVersion.split("\\.").length >= 2) {
try {
if (Integer.parseInt(serverVersion.split("\\.")[1]) >= 17) {
return controller.getJavaCommand();
}
} catch (NumberFormatException ignored) {}
}
return controller.getOldJavaCommand();
}
/**

View File

@ -117,9 +117,11 @@ public class ServerHandler {
public static void startServers() {
ServerLauncherController controller = Main.getController();
gui.setStatus("Starting servers");
int serverNum = 0;
Server previouslyStartedServer = null;
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.");
try {
stop();
@ -129,6 +131,9 @@ public class ServerHandler {
gui.updateGUIElementsWhenServersStartOrStop(false);
return;
}
if (server.isEnabled()) {
previouslyStartedServer = server;
}
}
}

View File

@ -134,7 +134,7 @@ public class ServerTypeHandler {
break;
case "Paper":
newType = new Paper("Paper", false, serverVersions, serverTypeInfo[2],
serverTypeInfo[3]);
serverTypeInfo[3], serverTypeInfo[4], serverTypeInfo[5]);
break;
case "Bungee":
newType = new BungeeCord("Bungee", true, serverVersions, serverTypeInfo[2],

View File

@ -22,6 +22,7 @@ public class ServerVersionContainer {
private String bungeeVersion;
private Map<String, String> waterfallVersions;
private Map<String, String> travertineVersions;
private Map<String, String> paperVersions;
private Map<String, String> spongeVanillaVersions;
private Map<String, String> spongeForgeVersions;
private String downloadedBuildToolsVersion;
@ -32,6 +33,7 @@ public class ServerVersionContainer {
private ServerVersionContainer() {
this.waterfallVersions = new HashMap<>();
this.travertineVersions = new HashMap<>();
this.paperVersions = new HashMap<>();
this.spongeVanillaVersions = new HashMap<>();
this.spongeForgeVersions = new HashMap<>();
loadState();
@ -58,6 +60,7 @@ public class ServerVersionContainer {
this.bungeeVersion = null;
this.waterfallVersions = new HashMap<>();
this.travertineVersions = new HashMap<>();
this.paperVersions = new HashMap<>();
this.spongeVanillaVersions = new HashMap<>();
this.spongeForgeVersions = new HashMap<>();
this.downloadedBuildToolsVersion = null;
@ -70,6 +73,7 @@ public class ServerVersionContainer {
"bungeeVersion;" + bungeeVersion + "\n" +
"waterfallVersions;" + mapToString(waterfallVersions) + "\n" +
"travertineVersions;" + mapToString(travertineVersions) + "\n" +
"paperVersions;" + mapToString(paperVersions) + "\n" +
"spongeVanillaVersions;" + mapToString(spongeVanillaVersions) + "\n" +
"spongeForgeVersions;" + mapToString(spongeForgeVersions) + "\n" +
"downloadedBuildToolsVersion;" + downloadedBuildToolsVersion;
@ -163,6 +167,9 @@ public class ServerVersionContainer {
case "travertineVersions":
parseVersionsToMap(travertineVersions, variableValue);
break;
case "paperVersions":
parseVersionsToMap(paperVersions, variableValue);
break;
case "spongeVanillaVersions":
parseVersionsToMap(spongeVanillaVersions, variableValue);
break;
@ -291,6 +298,27 @@ public class ServerVersionContainer {
saveState();
}
/**
* Gets a specific paper version
*
* @param versionKey <p>The version to check the current version of</p>
* @return <p>The current paper version</p>
*/
public String getPaperVersion(String versionKey) {
return this.paperVersions.get(versionKey);
}
/**
* Sets the current version for a given paper version
*
* @param mapKey <p>The version key to set version for</p>
* @param newValue <p>The new current version</p>
*/
public void setPaperVersion(String mapKey, String newValue) {
this.paperVersions.put(mapKey, newValue);
saveState();
}
/**
* Gets a specific sponge vanilla version
*

View File

@ -3,19 +3,25 @@ package net.knarcraft.minecraftserverlauncher.server.servertypes;
/**
* This class represents the Paper Minecraft server type
*/
public class Paper extends Spigot {
public class Paper extends Waterfall {
/**
* Instantiates a new Paper server type
*
* @param typeName <p>The name of the server type</p>
* @param isProxy <p>Whether this server type is a proxy server</p>
* @param versions <p>A list of one or more server versions for the type</p>
* @param downloadURL <p>The URL used for downloading .jar files</p>
* @param downloadURLPart <p>A string used after the download url as an additional part of the URL</p>
* @param typeName <p>The name of the server type</p>
* @param isProxy <p>Whether this server type is a proxy server</p>
* @param versions <p>The available versions for the server type</p>
* @param versionURL <p>The URL used to finding the newest version</p>
* @param srcStart <p>The string after which the version id starts</p>
* @param srcEnd <p>The string marking the end of the version id</p>
* @param downloadURL <p>The URL used for downloading the latest version</p>
*/
public Paper(String typeName, boolean isProxy, String[] versions, String downloadURL, String downloadURLPart) {
super(typeName, isProxy, versions, downloadURL, downloadURLPart);
public Paper(String typeName, boolean isProxy, String[] versions, String versionURL, String srcStart,
String srcEnd, String downloadURL) {
super(typeName, isProxy, versions, versionURL, srcStart, srcEnd, downloadURL);
this.oldVersionFunction = serverVersionContainer::getPaperVersion;
this.versionUpdateFunction = serverVersionContainer::setPaperVersion;
}
}

View File

@ -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();
}
}
}

View File

@ -93,7 +93,7 @@ public class Console extends KeyAdapter implements ActionListener, KeyListener {
StringBuilder newTextBuilder = new StringBuilder();
for (String line : oldTextList) {
if (!line.equals("")) {
newTextBuilder.append(line);
newTextBuilder.append(line).append("\n");
}
}
this.textOutput.setText(newTextBuilder.toString());

View File

@ -4,7 +4,7 @@ import net.knarcraft.minecraftserverlauncher.Main;
import net.knarcraft.minecraftserverlauncher.profile.Collection;
import net.knarcraft.minecraftserverlauncher.profile.ServerLauncherController;
import net.knarcraft.minecraftserverlauncher.server.ServerHandler;
import net.knarcraft.minecraftserverlauncher.utility.CommonFunctions;
import net.knarcraft.minecraftserverlauncher.utility.BackupUtil;
import javax.imageio.ImageIO;
import javax.naming.ConfigurationException;
@ -33,7 +33,7 @@ import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.getR
*/
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 Map<String, String> textStrings;
private Tray applicationTray;
@ -101,7 +101,7 @@ public class ServerLauncherGUI extends MessageHandler implements ActionListener,
@Override
public void setStatus(String text) {
this.lblStatuslabel.setText(text);
this.lblStatusLabel.setText(text);
this.logMessage(text);
}
@ -243,11 +243,11 @@ public class ServerLauncherGUI extends MessageHandler implements ActionListener,
panelBasic.add(profiles);
profiles.addActionListener(this);
sl_panel.putConstraint(SpringLayout.NORTH, lblStatuslabel, 6, SpringLayout.SOUTH, addProfileButton);
sl_panel.putConstraint(SpringLayout.SOUTH, lblStatuslabel, -10, SpringLayout.SOUTH, panelBasic);
sl_panel.putConstraint(SpringLayout.WEST, lblStatuslabel, 10, SpringLayout.WEST, panelBasic);
sl_panel.putConstraint(SpringLayout.EAST, lblStatuslabel, -10, SpringLayout.EAST, panelBasic);
panelBasic.add(lblStatuslabel);
sl_panel.putConstraint(SpringLayout.NORTH, lblStatusLabel, 6, SpringLayout.SOUTH, addProfileButton);
sl_panel.putConstraint(SpringLayout.SOUTH, lblStatusLabel, -10, SpringLayout.SOUTH, panelBasic);
sl_panel.putConstraint(SpringLayout.WEST, lblStatusLabel, 10, SpringLayout.WEST, panelBasic);
sl_panel.putConstraint(SpringLayout.EAST, lblStatusLabel, -10, SpringLayout.EAST, panelBasic);
panelBasic.add(lblStatusLabel);
addServerButton = new JButton("Add server");
sl_panel.putConstraint(SpringLayout.NORTH, addServerButton, 0, SpringLayout.NORTH, startServerButton);
@ -314,7 +314,7 @@ public class ServerLauncherGUI extends MessageHandler implements ActionListener,
@Override
public void actionPerformed(ActionEvent e) {
Object actionSource = e.getSource();
//Registers actions on the main tab
//Register actions on the main tab
handleMainTabButtons(actionSource);
}
@ -333,7 +333,8 @@ public class ServerLauncherGUI extends MessageHandler implements ActionListener,
} else if (actionSource == addServerButton) {
addServer();
} 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) {
controller.addProfile(JOptionPane.showInputDialog("Profile name: "));
updateProfiles();

View File

@ -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;
}
}
}

View File

@ -1,9 +1,6 @@
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.GUI;
import net.knarcraft.minecraftserverlauncher.userinterface.WebBrowser;
import java.io.BufferedReader;
@ -16,7 +13,6 @@ import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.URI;
import java.net.URISyntaxException;
@ -53,7 +49,7 @@ public final class CommonFunctions {
* @param folder <p>The folder to create</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.mkdirs()) {
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.
*
@ -295,34 +242,7 @@ public final class CommonFunctions {
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

View File

@ -1,2 +1,2 @@
beta
1.3.4
1.4.0
1 beta
2 1.3.4 1.4.0

View File

@ -1,6 +1,6 @@
Vanilla;Latest,Snapshot,1.17.1,1.16.5,1.15.2,1.14.4,1.13.2,1.12.2,1.11.2,1.10.2,1.9.4,1.8.9,1.7.10,1.6.4,1.5.2,1.4.7,1.3.2,1.2.5;https://launchermeta.mojang.com/mc/game/version_manifest.json;"release":";";https://s3.amazonaws.com/Minecraft.Download/versions/
Spigot;Latest,1.17.1,1.16.5,1.15.2,1.14.4,1.13.2,1.12.2,1.11.2,1.10.2,1.9.4,1.9.4,1.8.8,1.7.10,1.6.4-R2.1,1.5.2-R1.1,1.4.7-R1.1;https://static.knarcraft.net/archive/downloads/minecraftserverjars/Spigot/;spigot-
Paper;1.17.1,1.16.5,1.15.2,1.14.4,1.13.2,1.12.2,1.11.2,1.10.2,1.9.4,1.8.8;https://static.knarcraft.net/archive/downloads/minecraftserverjars/Paper/;Paper-
Paper;1.17.1,1.16.5,1.15.2,1.14.4,1.13.2,1.12.2,1.11.2,1.10.2,1.9.4,1.8.8;https://papermc.io/api/v1/paper/;"latest":;,;https://papermc.io/api/v1/paper/
#SpongeVanilla;1.12.2,1.11.2,1.10.2,1.8.9;https://dl-api.spongepowered.org/v1/org.spongepowered/spongevanilla/downloads?type=stable&minecraft=;"version":";",;https://repo.spongepowered.org/maven/org/spongepowered/spongevanilla/;/spongevanilla-
Craftbukkit;Latest,1.17.1,1.16.5,1.15.2,1.14.4,1.13.2,1.12.2,1.11.2,1.10.2,1.9.4,1.8.8,1.7.10-R0.1,1.6.4-R2.0,1.5.2-R1.0,1.4.6-R0.3,1.3.2-R3.0,1.2.5-R2.0,1.1-R6,1.0.1-R1,b1.8.1,b1.7.3;https://static.knarcraft.net/archive/downloads/minecraftserverjars/Bukkit/;craftbukkit-
#SpongeForge;1.12.2,1.11.2,1.10.2;https://dl-api.spongepowered.org/v1/org.spongepowered/spongeforge/downloads?type=stable&minecraft=;"version":";",;https://repo.spongepowered.org/maven/org/spongepowered/spongeforge/;/spongeforge-

Can't render this file because it contains an unexpected character in line 1 and column 200.

View File

@ -38,7 +38,7 @@ public class ServerVersionContainerTest {
serverVersionContainer.reset();
System.out.println(serverVersionContainer.toString());
assertEquals("vanillaVersion;null\nsnapshotVersion;null\nbungeeVersion;null\nwaterfallVersions;\n" +
"travertineVersions;\nspongeVanillaVersions;\nspongeForgeVersions;\ndownloadedBuildToolsVersion;null",
"travertineVersions;\npaperVersions;\nspongeVanillaVersions;\nspongeForgeVersions;\ndownloadedBuildToolsVersion;null",
serverVersionContainer.toString());
}