summaryrefslogtreecommitdiff
path: root/src/main/java/com/keuin/kbackupfabric/operation/RestoreOperation.java
blob: cc19627a2646284bdb7d85d341cbc443488f9b9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package com.keuin.kbackupfabric.operation;

import com.keuin.kbackupfabric.operation.abstracts.InvokableBlockingOperation;
import com.keuin.kbackupfabric.util.PrintUtil;
import com.keuin.kbackupfabric.util.ZipUtil;
import com.keuin.kbackupfabric.util.ZipUtilException;
import com.mojang.brigadier.context.CommandContext;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;

import java.io.File;
import java.io.IOException;

import static com.keuin.kbackupfabric.util.BackupFilesystemUtil.getBackupFileName;
import static com.keuin.kbackupfabric.util.BackupFilesystemUtil.getBackupSaveDirectory;
import static org.apache.commons.io.FileUtils.forceDelete;

public class RestoreOperation extends InvokableBlockingOperation {

    //private static final Logger LOGGER = LogManager.getLogger();
    private final String backupName;
    private final Thread serverThread;
    private final String backupFilePath;
    private final String levelDirectory;
    private final CommandContext<ServerCommandSource> context;
    private final MinecraftServer server;

    public RestoreOperation(CommandContext<ServerCommandSource> context, String backupFilePath, String levelDirectory, String backupName) {
        server = context.getSource().getMinecraftServer();
        this.backupName = backupName;
        this.serverThread = server.getThread();
        this.backupFilePath = backupFilePath;
        this.levelDirectory = levelDirectory;
        this.context = context;
    }

    @Override
    protected boolean blockingContext() {
        // do restore to backupName
        PrintUtil.broadcast(String.format("Restoring to previous world %s ...", backupName));

        String backupFileName = getBackupFileName(backupName);
        PrintUtil.debug("Backup file name: " + backupFileName);
        File backupFile = new File(getBackupSaveDirectory(server), backupFileName);

        PrintUtil.msgInfo(context, "Server will shutdown in a few seconds, depending on world size and disk speed, the progress may take from seconds to minutes.", true);
        PrintUtil.msgInfo(context, "Please do not force the server stop, or the level would be broken.", true);
        PrintUtil.msgInfo(context, "After it shuts down, please restart the server manually.", true);
        final int WAIT_SECONDS = 10;
        for (int i = 0; i < WAIT_SECONDS; ++i) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ignored) {
            }
        }
        PrintUtil.broadcast("Shutting down ...");
        //RestoreWorker worker = new RestoreWorker(server.getThread(), backupFilePath, levelDirectory);
        Thread workerThread = new Thread(new WorkerThread(), "RestoreWorker");
        workerThread.start();
        try {
            Thread.sleep(500);
        } catch (InterruptedException ignored) {
        }
        server.stop(false);
        return true;
    }

    @Override
    public String toString() {
        return String.format("restoration from %s", backupName);
    }

    private class WorkerThread implements Runnable {

        @Override
        public void run() {
            try {
                // Wait server thread die
                PrintUtil.info("Waiting for the server thread to exit ...");
                while (serverThread.isAlive()) {
                    try {
                        serverThread.join();
                    } catch (InterruptedException ignored) {
                    }
                }

                PrintUtil.info("Wait for 5 seconds ...");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException ignored) {
                }

                // Delete old level
                PrintUtil.info("Server stopped. Deleting old level ...");
                File levelDirFile = new File(levelDirectory);
                long startTime = System.currentTimeMillis();

                int failedCounter = 0;
                final int MAX_RETRY_TIMES = 20;
                while (failedCounter < MAX_RETRY_TIMES) {
                    System.gc();
                    if (!levelDirFile.delete() && levelDirFile.exists()) {
                        System.gc();
                        forceDelete(levelDirFile); // Try to force delete.
                    }
                    if (!levelDirFile.exists())
                        break;
                    ++failedCounter;
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException ignored) {
                    }
                }
                if (levelDirFile.exists()) {
                    PrintUtil.error(String.format("Cannot restore: failed to delete old level %s .", levelDirFile.getName()));
                    return;
                }

                // Decompress archive
                PrintUtil.info("Decompressing archived level ...");
                ZipUtil.unzip(backupFilePath, levelDirectory, false);
                long endTime = System.currentTimeMillis();
                PrintUtil.info(String.format("Restore complete! (%.2fs) Please restart the server manually.", (endTime - startTime) / 1000.0));
            } catch (SecurityException | IOException | ZipUtilException e) {
                PrintUtil.error("An exception occurred while restoring: " + e.getMessage());
            }
        }
    }
}