diff --git a/build.gradle b/build.gradle index 2b30f46..4a302db 100644 --- a/build.gradle +++ b/build.gradle @@ -11,9 +11,9 @@ apply plugin: 'net.minecraftforge.gradle.forge' //Only edit below this line, the above code adds and enables the necessary things for Forge to be setup. -version = "1.0" -group = "com.yourname.modid" // http://maven.apache.org/guides/mini/guide-naming-conventions.html -archivesBaseName = "modid" +version = "1.12.2-1.0" +group = "com.github.jinks.extbackup" // http://maven.apache.org/guides/mini/guide-naming-conventions.html +archivesBaseName = "ExtBackup" sourceCompatibility = targetCompatibility = '1.8' // Need this here so eclipse task generates correctly. compileJava { diff --git a/src/main/java/com/example/examplemod/ExampleMod.java b/src/main/java/com/example/examplemod/ExampleMod.java deleted file mode 100644 index 42a155b..0000000 --- a/src/main/java/com/example/examplemod/ExampleMod.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.example.examplemod; - -import net.minecraft.init.Blocks; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.Mod.EventHandler; -import net.minecraftforge.fml.common.event.FMLInitializationEvent; -import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; -import org.apache.logging.log4j.Logger; - -@Mod(modid = ExampleMod.MODID, name = ExampleMod.NAME, version = ExampleMod.VERSION) -public class ExampleMod -{ - public static final String MODID = "examplemod"; - public static final String NAME = "Example Mod"; - public static final String VERSION = "1.0"; - - private static Logger logger; - - @EventHandler - public void preInit(FMLPreInitializationEvent event) - { - logger = event.getModLog(); - } - - @EventHandler - public void init(FMLInitializationEvent event) - { - // some example code - logger.info("DIRT BLOCK >> {}", Blocks.DIRT.getRegistryName()); - } -} diff --git a/src/main/java/com/github/jinks/extbackup/BackupHandler.java b/src/main/java/com/github/jinks/extbackup/BackupHandler.java new file mode 100644 index 0000000..53fbefc --- /dev/null +++ b/src/main/java/com/github/jinks/extbackup/BackupHandler.java @@ -0,0 +1,150 @@ +package com.github.jinks.extbackup; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.StandardOpenOption; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.management.PlayerList; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.world.WorldServer; +import net.minecraft.world.storage.ThreadedFileIOBase; + +public enum BackupHandler { + INSTANCE; + + public long nextBackup = -1L; + public int doingBackup = 0; + public boolean hadPlayersOnline = false; + private boolean youHaveBeenWarned = false; + + public void init() { + doingBackup = 0; + nextBackup = System.currentTimeMillis() + ExtBackupConfig.general.time(); + File script = ExtBackupConfig.general.getScript(); + + if (!script.exists()) { + script.getParentFile().mkdirs(); + try { + Files.write(script.toPath(), "#!/bin/bash\n# Put your backup script here!\n\nexit 0".getBytes(StandardCharsets.UTF_8)); + script.setExecutable(true); + } catch (IOException e) { + ExtBackup.logger.error("Backup script does not exist and cannot be created!"); + ExtBackup.logger.error("Disabling ExtBackup!"); + ExtBackupConfig.general.enabled = false; + } + } + + ExtBackup.logger.info("Starting "+ExtBackup.NAME+" v"+ExtBackup.VERSION); + ExtBackup.logger.info("Active script: " + script.getAbsolutePath()); + ExtBackup.logger.info("Next Backup at: " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date(nextBackup))); + } + + public boolean run(MinecraftServer server) { + if (doingBackup != 0 || !ExtBackupConfig.general.enabled) { + return false; + } + if (doingBackup != 0) { + ExtBackup.logger.warn("Tried to start backup while one is already running!"); + return false; + } + + File script = ExtBackupConfig.general.getScript(); + if (!script.exists() || !script.canExecute()) { + ExtBackup.logger.error("Cannot access or execute backup script. Bailing out!"); + return false; + } + + doingBackup = 1; + + ThreadedFileIOBase.getThreadedIOInstance().queueIO(() -> { + try { + doBackup(server, script); + } catch (Exception ex) { + ex.printStackTrace(); + } + + doingBackup = 2; + return false; + }); + + nextBackup = System.currentTimeMillis() + ExtBackupConfig.general.time(); + return true; + } + + private void doBackup(MinecraftServer server, File script) { + ExtBackup.logger.info("Starting backup."); + PlayerList pl = server.getPlayerList(); + ExtBackupUtil.broadcast(server, "Starting Backup!"); + try { + if (server.getPlayerList() != null) { + server.getPlayerList().saveAllPlayerData(); + } + + for (WorldServer world : server.worlds) { + if (world != null) { + world.saveAllChunks(true, null); + world.flushToDisk(); + world.disableLevelSaving = true; + } + } + } catch (Exception ex) { + ExtBackup.logger.error("Saving the world failed!"); + enableSaving(server); + ex.printStackTrace(); + return; + } + + ProcessBuilder pb = new ProcessBuilder(script.getAbsolutePath()); + int returnValue = -1; + Map env = pb.environment(); + pb.redirectErrorStream(true); + try { + Process backup = pb.start(); + returnValue = backup.waitFor(); + } catch (Exception ex) { + enableSaving(server); + ExtBackup.logger.error("Something went wrong with the Backup script!"); + ExtBackup.logger.error("Check your Backups."); + ex.printStackTrace(); + } + enableSaving(server); + youHaveBeenWarned = false; + ExtBackup.logger.info("Backup done."); + ExtBackupUtil.broadcast(server, "Backup done!"); + } + + private void enableSaving(MinecraftServer server) { + for (WorldServer world : server.worlds) { + if (world != null) { + world.disableLevelSaving = false; + } + } + } + + public void tick(MinecraftServer server, long now) { + if (nextBackup > 0L && nextBackup <= now) { + //ExtBackup.logger.info("Backup time!"); + if (!ExtBackupConfig.general.only_if_players_online || hadPlayersOnline || !server.getPlayerList().getPlayers().isEmpty()) { + hadPlayersOnline = false; + run(server); + } + } + if (doingBackup > 1) { + doingBackup = 0; + } else if (doingBackup > 0) { + if (now - nextBackup > 1200000 && !youHaveBeenWarned) { + ExtBackup.logger.warn("There has been a running backup for more than 20 minutes."); + ExtBackup.logger.warn("Something seems to be wrong."); + youHaveBeenWarned = true; + } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/github/jinks/extbackup/ExtBackup.java b/src/main/java/com/github/jinks/extbackup/ExtBackup.java new file mode 100644 index 0000000..fbc6c41 --- /dev/null +++ b/src/main/java/com/github/jinks/extbackup/ExtBackup.java @@ -0,0 +1,70 @@ +package com.github.jinks.extbackup; + +import org.apache.logging.log4j.Logger; + +import net.minecraft.init.Blocks; +import net.minecraft.server.MinecraftServer; +import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.common.event.FMLServerStartedEvent; +import net.minecraftforge.fml.common.event.FMLServerStoppingEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.PlayerEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; + +@Mod(modid = ExtBackup.MODID, name = ExtBackup.NAME, version = ExtBackup.VERSION, acceptableRemoteVersions = "*") +@Mod.EventBusSubscriber +public class ExtBackup { + + public static final String MODID = "extbackup"; + public static final String NAME = "ExtBackup"; + public static final String VERSION = "1.0"; + + public static Logger logger; + + @Mod.EventHandler + public void preInit(FMLPreInitializationEvent event) { + logger = event.getModLog(); + } + + @Mod.EventHandler + public void init(FMLInitializationEvent event) { + // some example code + //logger.info("DIRT BLOCK >> {}", Blocks.DIRT.getRegistryName()); + } + + @Mod.EventHandler + public void serverStarted(FMLServerStartedEvent event) { + BackupHandler.INSTANCE.init(); + } + + @Mod.EventHandler + public void serverStopping(FMLServerStoppingEvent event) { + if (ExtBackupConfig.general.force_on_shutdown) { + MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); + + if (server != null) { + BackupHandler.INSTANCE.run(server); + } + } + } + + @SubscribeEvent + public static void serverTick(TickEvent.ServerTickEvent event) { + if (event.phase != TickEvent.Phase.START) { + MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); + + if (server != null) { + //logger.debug("Server Tick! " + event.phase); + BackupHandler.INSTANCE.tick(server, System.currentTimeMillis()); + } + } + } + + @SubscribeEvent + public static void playerLoggedOut(PlayerEvent.PlayerLoggedOutEvent event) { + BackupHandler.INSTANCE.hadPlayersOnline = true; + } +} \ No newline at end of file diff --git a/src/main/java/com/github/jinks/extbackup/ExtBackupConfig.java b/src/main/java/com/github/jinks/extbackup/ExtBackupConfig.java new file mode 100644 index 0000000..353f63f --- /dev/null +++ b/src/main/java/com/github/jinks/extbackup/ExtBackupConfig.java @@ -0,0 +1,73 @@ +package com.github.jinks.extbackup; + +import net.minecraftforge.fml.client.config.ConfigGuiType; +import net.minecraftforge.fml.client.event.ConfigChangedEvent; +import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + +import java.io.File; + +import net.minecraftforge.common.config.Config; +import net.minecraftforge.common.config.ConfigManager; + +@EventBusSubscriber(modid = ExtBackup.MODID) +@Config(modid = ExtBackup.MODID, category = "") +public class ExtBackupConfig { + public static final General general = new General(); + + public static class General { + @Config.Comment("Enables backups.") + public boolean enabled = true; + + @Config.RangeInt(min = 1, max = 3600) + @Config.Comment({ + "Timer in Minutes.", + " 5 - backups every 5 minutes", + " 60 - backups every hour", + "3600 - backups once a day", + }) + public int backup_timer = 10; + + @Config.Comment("If set to true, no messages will be displayed in chat/status bar.") + public boolean silent = false; + + @Config.Comment("Only create backups when players have been online.") + public boolean only_if_players_online = true; + + @Config.Comment("Create a backup when server is stopped.") + public boolean force_on_shutdown = true; + + @Config.Comment({ + "Path to backup script.", + "Default: 'local/extbackup/runbackup.sh' inside the Minecraft instance."}) + public String script = "local/extbackup/runbackup.sh"; + + public long time() { + return (long) (backup_timer * 60000L); + } + + private File cachedScript; + public File getScript() { + if (cachedScript == null) { + cachedScript = ExtBackupConfig.general.script.trim().isEmpty() ? new File(FMLCommonHandler.instance().getMinecraftServerInstance().getDataDirectory(), "local/extbackup/runbackup.sh") : new File(ExtBackupConfig.general.script.trim()); + } + return cachedScript; + } + } + + public static boolean sync() { + ConfigManager.sync(ExtBackup.MODID, Config.Type.INSTANCE); + general.cachedScript = null; + return true; + } + + @SubscribeEvent + public static void onConfigChanged(ConfigChangedEvent.OnConfigChangedEvent event) + { + if (event.getModID().equals(ExtBackup.MODID)) { + sync(); + } + } + +} diff --git a/src/main/java/com/github/jinks/extbackup/ExtBackupUtil.java b/src/main/java/com/github/jinks/extbackup/ExtBackupUtil.java new file mode 100644 index 0000000..4f6c86c --- /dev/null +++ b/src/main/java/com/github/jinks/extbackup/ExtBackupUtil.java @@ -0,0 +1,15 @@ +package com.github.jinks.extbackup; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.text.TextComponentString; + +public class ExtBackupUtil { + + public static void broadcast(MinecraftServer server, String message) { + if (!ExtBackupConfig.general.silent) { + TextComponentString bcMessage = new TextComponentString("[§1ExtBackup§r] " + message); + server.getPlayerList().sendMessage(bcMessage); + } + } + +}