mirror of
				https://github.com/GayPizzaSpecifications/foundation.git
				synced 2025-11-04 11:39:39 +00:00 
			
		
		
		
	Basic world backup command.
This commit is contained in:
		@ -1,18 +1,38 @@
 | 
				
			|||||||
package cloud.kubelet.foundation;
 | 
					package cloud.kubelet.foundation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import cloud.kubelet.foundation.command.BackupCommand;
 | 
				
			||||||
import io.papermc.paper.event.player.ChatEvent;
 | 
					import io.papermc.paper.event.player.ChatEvent;
 | 
				
			||||||
 | 
					import java.io.File;
 | 
				
			||||||
 | 
					import java.nio.file.Path;
 | 
				
			||||||
 | 
					import java.nio.file.Paths;
 | 
				
			||||||
 | 
					import java.util.Objects;
 | 
				
			||||||
import net.kyori.adventure.text.Component;
 | 
					import net.kyori.adventure.text.Component;
 | 
				
			||||||
import org.bukkit.event.EventHandler;
 | 
					import org.bukkit.event.EventHandler;
 | 
				
			||||||
import org.bukkit.event.Listener;
 | 
					import org.bukkit.event.Listener;
 | 
				
			||||||
import org.bukkit.plugin.java.JavaPlugin;
 | 
					import org.bukkit.plugin.java.JavaPlugin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public final class Foundation extends JavaPlugin implements Listener {
 | 
					public final class Foundation extends JavaPlugin implements Listener {
 | 
				
			||||||
 | 
					  public static final boolean BACKUP_ENABLED = true;
 | 
				
			||||||
 | 
					  private static final String BACKUPS_DIRECTORY = "backups";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public void onEnable() {
 | 
					  public void onEnable() {
 | 
				
			||||||
 | 
					    Path dataPath = getDataFolder().toPath();
 | 
				
			||||||
 | 
					    Path backupPath = dataPath.resolve(BACKUPS_DIRECTORY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create Foundation plugin directories.
 | 
				
			||||||
 | 
					    dataPath.toFile().mkdir();
 | 
				
			||||||
 | 
					    backupPath.toFile().mkdir();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Register this as an event listener.
 | 
				
			||||||
    getServer().getPluginManager().registerEvents(this, this);
 | 
					    getServer().getPluginManager().registerEvents(this, this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getDataFolder().mkdir();
 | 
					    // Set up commands.
 | 
				
			||||||
 | 
					    Objects.requireNonNull(getCommand("fbackup")).setExecutor(new BackupCommand(backupPath));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final var log = getSLF4JLogger();
 | 
				
			||||||
 | 
					    log.info("Features:");
 | 
				
			||||||
 | 
					    Util.printFeatureStatus(log, "Backup: ", BACKUP_ENABLED);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										15
									
								
								src/main/java/cloud/kubelet/foundation/Util.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/main/java/cloud/kubelet/foundation/Util.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					package cloud.kubelet.foundation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class Util {
 | 
				
			||||||
 | 
					  public static int runProcess(List<String> command) throws IOException, InterruptedException {
 | 
				
			||||||
 | 
					    return new ProcessBuilder(command).start().waitFor();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public static void printFeatureStatus(Logger logger, String feature, boolean state) {
 | 
				
			||||||
 | 
					    logger.info("{}: {}", feature, state ? "Enabled" : "Disabled");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,105 @@
 | 
				
			|||||||
 | 
					package cloud.kubelet.foundation.command;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import cloud.kubelet.foundation.Foundation;
 | 
				
			||||||
 | 
					import java.io.BufferedOutputStream;
 | 
				
			||||||
 | 
					import java.io.FileInputStream;
 | 
				
			||||||
 | 
					import java.io.FileOutputStream;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.io.InputStream;
 | 
				
			||||||
 | 
					import java.nio.file.Files;
 | 
				
			||||||
 | 
					import java.nio.file.Path;
 | 
				
			||||||
 | 
					import java.nio.file.Paths;
 | 
				
			||||||
 | 
					import java.time.Instant;
 | 
				
			||||||
 | 
					import java.util.concurrent.atomic.AtomicBoolean;
 | 
				
			||||||
 | 
					import java.util.zip.ZipEntry;
 | 
				
			||||||
 | 
					import java.util.zip.ZipOutputStream;
 | 
				
			||||||
 | 
					import net.kyori.adventure.text.Component;
 | 
				
			||||||
 | 
					import net.kyori.adventure.text.format.TextColor;
 | 
				
			||||||
 | 
					import org.bukkit.World;
 | 
				
			||||||
 | 
					import org.bukkit.command.Command;
 | 
				
			||||||
 | 
					import org.bukkit.command.CommandExecutor;
 | 
				
			||||||
 | 
					import org.bukkit.command.CommandSender;
 | 
				
			||||||
 | 
					import org.jetbrains.annotations.NotNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class BackupCommand implements CommandExecutor {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static final AtomicBoolean RUNNING = new AtomicBoolean();
 | 
				
			||||||
 | 
					  private final Path backupPath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public BackupCommand(Path backupPath) {
 | 
				
			||||||
 | 
					    this.backupPath = backupPath;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
 | 
				
			||||||
 | 
					      @NotNull String label, @NotNull String[] args) {
 | 
				
			||||||
 | 
					    if (!Foundation.BACKUP_ENABLED) {
 | 
				
			||||||
 | 
					      sender.sendMessage(
 | 
				
			||||||
 | 
					          Component
 | 
				
			||||||
 | 
					              .text("Backup is not enabled.")
 | 
				
			||||||
 | 
					              .color(TextColor.fromHexString("#FF0000"))
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (RUNNING.get()) {
 | 
				
			||||||
 | 
					      sender.sendMessage(
 | 
				
			||||||
 | 
					          Component
 | 
				
			||||||
 | 
					              .text("Backup is already running.")
 | 
				
			||||||
 | 
					              .color(TextColor.fromHexString("#FF0000"))
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        runBackup(sender);
 | 
				
			||||||
 | 
					      } catch (Exception e) {
 | 
				
			||||||
 | 
					        sender.sendMessage(String.format("Failed to backup: %s", e.getMessage()));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void runBackup(CommandSender sender) throws IOException {
 | 
				
			||||||
 | 
					    RUNNING.set(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final var backupFile = backupPath.resolve(
 | 
				
			||||||
 | 
					        String.format("backup-%s.zip", Instant.now().toString())).toFile();
 | 
				
			||||||
 | 
					    final var zipFileStream = new FileOutputStream(backupFile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try (zipFileStream; var zipStream = new ZipOutputStream(
 | 
				
			||||||
 | 
					        new BufferedOutputStream(zipFileStream))) {
 | 
				
			||||||
 | 
					      final var worlds = sender.getServer().getWorlds();
 | 
				
			||||||
 | 
					      for (World world : worlds) {
 | 
				
			||||||
 | 
					        final var name = world.getName();
 | 
				
			||||||
 | 
					        final var worldPath = world.getWorldFolder().toPath().toString();
 | 
				
			||||||
 | 
					        sender.sendMessage(String.format("%s: %s", name, worldPath));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        world.save();
 | 
				
			||||||
 | 
					        world.setAutoSave(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					          final var paths = Files.walk(Paths.get(worldPath))
 | 
				
			||||||
 | 
					              .filter(Files::isRegularFile)
 | 
				
			||||||
 | 
					              .toList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          for (Path path : paths) {
 | 
				
			||||||
 | 
					            try (InputStream fileStream = new FileInputStream(path.toFile())) {
 | 
				
			||||||
 | 
					              final var entry = new ZipEntry(path.toString());
 | 
				
			||||||
 | 
					              zipStream.putNextEntry(entry);
 | 
				
			||||||
 | 
					              int n;
 | 
				
			||||||
 | 
					              byte[] buffer = new byte[1024];
 | 
				
			||||||
 | 
					              while ((n = fileStream.read(buffer)) > -1) {
 | 
				
			||||||
 | 
					                zipStream.write(buffer, 0, n);
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					          e.printStackTrace();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        world.setAutoSave(true);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } finally {
 | 
				
			||||||
 | 
					      RUNNING.set(false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -6,4 +6,8 @@ prefix: Foundation
 | 
				
			|||||||
load: STARTUP
 | 
					load: STARTUP
 | 
				
			||||||
authors:
 | 
					authors:
 | 
				
			||||||
  - kubelet
 | 
					  - kubelet
 | 
				
			||||||
commands: {}
 | 
					commands:
 | 
				
			||||||
 | 
					  fbackup:
 | 
				
			||||||
 | 
					    description: Foundation Backup
 | 
				
			||||||
 | 
					    usage: /fbackup
 | 
				
			||||||
 | 
					    permission: foundation.backup
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user