Code/net/minecraft/client/gui/screens/RecoverWorldDataScreen.java

165 lines
8.3 KiB
Java
Raw Normal View History

2025-07-01 06:20:03 +00:00
package net.minecraft.client.gui.screens;
import com.mojang.logging.LogUtils;
import it.unimi.dsi.fastutil.booleans.BooleanConsumer;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.time.Instant;
import javax.annotation.Nullable;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.MultiLineTextWidget;
import net.minecraft.client.gui.components.StringWidget;
import net.minecraft.client.gui.components.Tooltip;
import net.minecraft.client.gui.layouts.FrameLayout;
import net.minecraft.client.gui.layouts.LinearLayout;
import net.minecraft.client.gui.screens.worldselection.EditWorldScreen;
import net.minecraft.client.gui.screens.worldselection.WorldSelectionList;
import net.minecraft.nbt.NbtException;
import net.minecraft.nbt.ReportedNbtException;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.util.CommonLinks;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.slf4j.Logger;
@OnlyIn(Dist.CLIENT)
public class RecoverWorldDataScreen extends Screen {
private static final Logger LOGGER = LogUtils.getLogger();
private static final int SCREEN_SIDE_MARGIN = 25;
private static final Component TITLE = Component.translatable("recover_world.title").withStyle(ChatFormatting.BOLD);
private static final Component BUGTRACKER_BUTTON = Component.translatable("recover_world.bug_tracker");
private static final Component RESTORE_BUTTON = Component.translatable("recover_world.restore");
private static final Component NO_FALLBACK_TOOLTIP = Component.translatable("recover_world.no_fallback");
private static final Component DONE_TITLE = Component.translatable("recover_world.done.title");
private static final Component DONE_SUCCESS = Component.translatable("recover_world.done.success");
private static final Component DONE_FAILED = Component.translatable("recover_world.done.failed");
private static final Component NO_ISSUES = Component.translatable("recover_world.issue.none").withStyle(ChatFormatting.GREEN);
private static final Component MISSING_FILE = Component.translatable("recover_world.issue.missing_file").withStyle(ChatFormatting.RED);
private final BooleanConsumer callback;
private final LinearLayout layout = LinearLayout.vertical().spacing(8);
private final Component message;
private final MultiLineTextWidget messageWidget;
private final MultiLineTextWidget issuesWidget;
private final LevelStorageSource.LevelStorageAccess storageAccess;
public RecoverWorldDataScreen(Minecraft p_310416_, BooleanConsumer p_312140_, LevelStorageSource.LevelStorageAccess p_310102_) {
super(TITLE);
this.callback = p_312140_;
this.message = Component.translatable("recover_world.message", Component.literal(p_310102_.getLevelId()).withStyle(ChatFormatting.GRAY));
this.messageWidget = new MultiLineTextWidget(this.message, p_310416_.font);
this.storageAccess = p_310102_;
Exception exception = this.collectIssue(p_310102_, false);
Exception exception1 = this.collectIssue(p_310102_, true);
Component component = Component.empty()
.append(this.buildInfo(p_310102_, false, exception))
.append("\n")
.append(this.buildInfo(p_310102_, true, exception1));
this.issuesWidget = new MultiLineTextWidget(component, p_310416_.font);
boolean flag = exception != null && exception1 == null;
this.layout.defaultCellSetting().alignHorizontallyCenter();
this.layout.addChild(new StringWidget(this.title, p_310416_.font));
this.layout.addChild(this.messageWidget.setCentered(true));
this.layout.addChild(this.issuesWidget);
LinearLayout linearlayout = LinearLayout.horizontal().spacing(5);
linearlayout.addChild(Button.builder(BUGTRACKER_BUTTON, ConfirmLinkScreen.confirmLink(this, CommonLinks.SNAPSHOT_BUGS_FEEDBACK)).size(120, 20).build());
linearlayout.addChild(
Button.builder(RESTORE_BUTTON, p_311022_ -> this.attemptRestore(p_310416_))
.size(120, 20)
.tooltip(flag ? null : Tooltip.create(NO_FALLBACK_TOOLTIP))
.build()
)
.active = flag;
this.layout.addChild(linearlayout);
this.layout.addChild(Button.builder(CommonComponents.GUI_BACK, p_311773_ -> this.onClose()).size(120, 20).build());
this.layout.visitWidgets(this::addRenderableWidget);
}
private void attemptRestore(Minecraft p_311355_) {
Exception exception = this.collectIssue(this.storageAccess, false);
Exception exception1 = this.collectIssue(this.storageAccess, true);
if (exception != null && exception1 == null) {
p_311355_.forceSetScreen(new GenericMessageScreen(Component.translatable("recover_world.restoring")));
EditWorldScreen.makeBackupAndShowToast(this.storageAccess);
if (this.storageAccess.restoreLevelDataFromOld()) {
p_311355_.setScreen(new ConfirmScreen(this.callback, DONE_TITLE, DONE_SUCCESS, CommonComponents.GUI_CONTINUE, CommonComponents.GUI_BACK));
} else {
p_311355_.setScreen(new AlertScreen(() -> this.callback.accept(false), DONE_TITLE, DONE_FAILED));
}
} else {
LOGGER.error(
"Failed to recover world, files not as expected. level.dat: {}, level.dat_old: {}",
exception != null ? exception.getMessage() : "no issues",
exception1 != null ? exception1.getMessage() : "no issues"
);
p_311355_.setScreen(new AlertScreen(() -> this.callback.accept(false), DONE_TITLE, DONE_FAILED));
}
}
private Component buildInfo(LevelStorageSource.LevelStorageAccess p_311955_, boolean p_311169_, @Nullable Exception p_312117_) {
if (p_311169_ && p_312117_ instanceof FileNotFoundException) {
return Component.empty();
} else {
MutableComponent mutablecomponent = Component.empty();
Instant instant = p_311955_.getFileModificationTime(p_311169_);
MutableComponent mutablecomponent1 = instant != null
? Component.literal(WorldSelectionList.DATE_FORMAT.format(instant))
: Component.translatable("recover_world.state_entry.unknown");
mutablecomponent.append(Component.translatable("recover_world.state_entry", mutablecomponent1.withStyle(ChatFormatting.GRAY)));
if (p_312117_ == null) {
mutablecomponent.append(NO_ISSUES);
} else if (p_312117_ instanceof FileNotFoundException) {
mutablecomponent.append(MISSING_FILE);
} else if (p_312117_ instanceof ReportedNbtException) {
mutablecomponent.append(Component.literal(p_312117_.getCause().toString()).withStyle(ChatFormatting.RED));
} else {
mutablecomponent.append(Component.literal(p_312117_.toString()).withStyle(ChatFormatting.RED));
}
return mutablecomponent;
}
}
@Nullable
private Exception collectIssue(LevelStorageSource.LevelStorageAccess p_311404_, boolean p_311931_) {
try {
if (!p_311931_) {
p_311404_.getSummary(p_311404_.getDataTag());
} else {
p_311404_.getSummary(p_311404_.getDataTagFallback());
}
return null;
} catch (NbtException | ReportedNbtException | IOException ioexception) {
return ioexception;
}
}
@Override
protected void init() {
super.init();
this.repositionElements();
}
@Override
protected void repositionElements() {
this.issuesWidget.setMaxWidth(this.width - 50);
this.messageWidget.setMaxWidth(this.width - 50);
this.layout.arrangeElements();
FrameLayout.centerInRectangle(this.layout, this.getRectangle());
}
@Override
public Component getNarrationMessage() {
return CommonComponents.joinForNarration(super.getNarrationMessage(), this.message);
}
@Override
public void onClose() {
this.callback.accept(false);
}
}