Files
Lophine/lophine-server/src/main/java/fun/bm/lophine/utils/SaveAllUtil.java
T
2026-03-19 18:39:37 +08:00

91 lines
4.3 KiB
Java

package fun.bm.lophine.utils;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.datafixers.util.Pair;
import fun.bm.lophine.config.modules.experiment.CommandConfig;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.commands.SaveAllCommand;
import net.minecraft.server.level.ServerLevel;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public class SaveAllUtil {
public static final Object lock = new Object();
public static volatile long lastSaveAllTime = 0;
private static volatile Pair<CommandSourceStack, Boolean> currentSaveAll = null;
private static volatile int regionCount = 0;
private static final AtomicInteger savedRegionCount = new AtomicInteger(0);
private static final AtomicBoolean withError = new AtomicBoolean(false);
public static void preSaveAll(CommandSourceStack source, boolean flush) {
synchronized (lock) {
if (isSaving()) {
source.sendFailure(Component.literal("Server is already in saving! Please wait..."));
} else {
source.sendSuccess(() -> Component.translatable("commands.save.saving"), false);
currentSaveAll = Pair.of(source, flush);
// we need to count how many regions we need to save
AtomicInteger count = new AtomicInteger();
for (ServerLevel world : source.getServer().getAllLevels()) {
world.regioniser.computeForAllRegions(unused -> {
count.getAndIncrement();
});
}
regionCount = count.get();
savedRegionCount.set(0);
withError.set(false);
lastSaveAllTime = System.currentTimeMillis();
}
}
}
public static void postRegionSave(io.papermc.paper.threadedregions.TickRegions.TickRegionData region) {
Pair<CommandSourceStack, Boolean> currentSaveAll = SaveAllUtil.currentSaveAll;
synchronized (lock) {
if (lastSaveAllTime < region.lastSavedTime) return; // already saved
}
try {
region.world.moonrise$getChunkTaskScheduler().chunkHolderManager.saveAllChunksRegionised(currentSaveAll.getSecond(), false, CommandConfig.logAllProcess, false, false, true, false);
currentSaveAll.getFirst().getServer().getPlayerList().saveAll();
MinecraftServer.LOGGER.info("Saved chunks in region around chunk {} in world '{}'", region.region.getCenterChunk(), region.region.regioniser.world.getWorld().getName());
} catch (final Throwable thr) {
CommandSyntaxException error = SaveAllCommand.getErrorFailed().create();
currentSaveAll.getFirst().sendFailure(Component.literal(error.getMessage()));
MinecraftServer.LOGGER.error(error.getMessage(), thr);
withError.set(true);
}
region.lastSavedTime = System.currentTimeMillis();
int saved;
synchronized (savedRegionCount) {
saved = savedRegionCount.incrementAndGet();
}
if (saved >= regionCount) {
if (withError.get()) {
currentSaveAll.getFirst().sendFailure(Component.literal("At least one region failed to save!"));
} else if (saved == regionCount) {
currentSaveAll.getFirst().sendSuccess(() -> Component.translatable("commands.save.success"), true);
SaveAllUtil.currentSaveAll = null;
}
}
}
public static void checkTimeout() {
Pair<CommandSourceStack, Boolean> currentSaveAll = SaveAllUtil.currentSaveAll;
if (!isSaving()) return;
if (System.currentTimeMillis() - lastSaveAllTime > CommandConfig.saveAllTimeout) {
currentSaveAll.getFirst().sendFailure(Component.literal("At least one region save data timeout!"));
currentSaveAll.getFirst().sendFailure(Component.literal("Regions need to save expect is " + regionCount + ", but only " + savedRegionCount.get() + " saved!"));
SaveAllUtil.currentSaveAll = null;
}
}
public static boolean isSaving() {
synchronized (lock) {
return currentSaveAll != null;
}
}
}