diff --git a/src/main/java/ir/mehradn/rollback/gui/RollbackListWidget.java b/src/main/java/ir/mehradn/rollback/gui/RollbackListWidget.java index bf63009..1617043 100644 --- a/src/main/java/ir/mehradn/rollback/gui/RollbackListWidget.java +++ b/src/main/java/ir/mehradn/rollback/gui/RollbackListWidget.java @@ -24,14 +24,16 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Date; import java.util.List; import java.util.Optional; @Environment(EnvType.CLIENT) -public final class RollbackListWidget extends AlwaysSelectedEntryListWidget { +public final class RollbackListWidget extends AlwaysSelectedEntryListWidget { private final RollbackScreen screen; private final BackupManager backupManager; private final LevelSummary summary; + private final CurrentSaveEntry currentSaveEntry; private boolean shouldReloadEntries; public RollbackListWidget(RollbackScreen screen, BackupManager backupManager, LevelSummary levelSummary, MinecraftClient minecraftClient, @@ -40,6 +42,7 @@ public RollbackListWidget(RollbackScreen screen, BackupManager backupManager, Le this.screen = screen; this.backupManager = backupManager; this.summary = levelSummary; + this.currentSaveEntry = new CurrentSaveEntry(); this.shouldReloadEntries = true; } @@ -51,9 +54,12 @@ public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { private void reloadEntries() { clearEntries(); + addEntry(this.currentSaveEntry); + List backups = this.backupManager.getRollbacksFor(this.summary.getName()); for (int i = 1; i <= backups.size(); i++) addEntry(new RollbackEntry(i, backups.get(backups.size() - i))); + this.screen.narrateScreenIfNarrationEnabled(true); this.shouldReloadEntries = false; } @@ -66,50 +72,34 @@ public int getRowWidth() { return super.getRowWidth() + 50; } - public void setSelected(RollbackEntry entry) { + public void setSelected(Entry entry) { super.setSelected(entry); - this.screen.setEntrySelected(entry != null); + this.screen.setEntrySelected(entry != null, entry != null && entry != this.currentSaveEntry); } - public Optional getSelectedAsOptional() { - RollbackEntry entry = getSelectedOrNull(); + public Optional getSelectedAsOptional() { + Entry entry = getSelectedOrNull(); if (entry != null) return Optional.of(entry); return Optional.empty(); } @Environment(EnvType.CLIENT) - public final class RollbackEntry extends AlwaysSelectedEntryListWidget.Entry implements AutoCloseable { - private static final Identifier UNKNOWN_SERVER_LOCATION = new Identifier("textures/misc/unknown_server.png"); - private static final Identifier WORLD_SELECTION_LOCATION = new Identifier("textures/gui/world_selection.png"); - private final MinecraftClient client; - private final int backupNumber; - private final RollbackBackup backup; - private final Identifier iconLocation; - private final NativeImageBackedTexture icon; + public abstract class Entry extends AlwaysSelectedEntryListWidget.Entry implements AutoCloseable { + protected static final Identifier UNKNOWN_SERVER_LOCATION = new Identifier("textures/misc/unknown_server.png"); + protected static final Identifier WORLD_SELECTION_LOCATION = new Identifier("textures/gui/world_selection.png"); + protected final MinecraftClient client; + protected Identifier iconLocation; + protected NativeImageBackedTexture icon; - public RollbackEntry(int backupNumber, RollbackBackup rollbackBackup) { + public Entry() { this.client = RollbackListWidget.this.client; - this.backupNumber = backupNumber; - this.backup = rollbackBackup; - this.iconLocation = new Identifier("rollback", "backup/" + this.backupNumber + "/icon.png"); - this.icon = getIconTexture(); } - public Text getNarration() { - return Text.translatable("rollback.narrator.selectRollback", - this.backupNumber, - this.backup.getDateAsString(), - this.backup.daysPlayed - ); - } - - public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, - int mouseX, int mouseY, boolean hovered, float tickDelta) { - Text title = Text.translatable("rollback.day", this.backup.daysPlayed); - Text created = Text.translatable("rollback.created", this.backup.getDateAsString()); + protected void render(Text title, Text info, + MatrixStack matrices, int y, int x, int mouseX, boolean hovered) { this.client.textRenderer.draw(matrices, title, x + 35, y + 1, 0xFFFFFF); - this.client.textRenderer.draw(matrices, created, x + 35, y + this.client.textRenderer.fontHeight + 3, 0x808080); + this.client.textRenderer.draw(matrices, info, x + 35, y + this.client.textRenderer.fontHeight + 3, 0x808080); RenderSystem.setShader(GameRenderer::getPositionTexProgram); RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f); @@ -138,7 +128,110 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { return false; } - public NativeImageBackedTexture getIconTexture() { + public void close() { + if (this.icon != null) + this.icon.close(); + } + + public abstract void play(); + + public abstract void delete(); + } + + @Environment(EnvType.CLIENT) + public final class CurrentSaveEntry extends Entry { + private final LevelSummary summary; + private final String lastPlayed; + + public CurrentSaveEntry() { + super(); + this.summary = RollbackListWidget.this.summary; + this.lastPlayed = RollbackBackup.DATE_FORMAT.format(new Date(this.summary.getLastPlayed())); + this.iconLocation = new Identifier("rollback", "backup/current_save.png"); + this.icon = getIconTexture(); + } + + public Text getNarration() { + return Text.translatable( + "rollback.narrator.selectCurrentSave", + this.lastPlayed + ); + } + + public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, + int mouseX, int mouseY, boolean hovered, float tickDelta) { + super.render( + Text.translatable("rollback.screen.currentSave"), + Text.translatable("rollback.screen.lastPlayed", this.lastPlayed), + matrices, y, x, mouseX, hovered + ); + } + + private NativeImageBackedTexture getIconTexture() { + Path path = this.summary.getIconPath(); + if (!Files.isRegularFile(path)) { + this.client.getTextureManager().destroyTexture(this.iconLocation); + return null; + } + + Rollback.LOGGER.debug("Loading the world icon..."); + try (InputStream inputStream = Files.newInputStream(path)) { + NativeImage image = NativeImage.read(inputStream); + Validate.validState(image.getWidth() == 64, "Must be 64 pixels wide"); + Validate.validState(image.getHeight() == 64, "Must be 64 pixels high"); + + NativeImageBackedTexture texture = new NativeImageBackedTexture(image); + this.client.getTextureManager().registerTexture(this.iconLocation, texture); + return texture; + } catch (IOException e) { + Rollback.LOGGER.error("Failed to load the world icon!", e); + this.client.getTextureManager().destroyTexture(this.iconLocation); + return null; + } + } + + public void play() { + PublicStatics.playWorld = RollbackListWidget.this.summary; + PublicStatics.rollbackWorld = null; + PublicStatics.recreateWorld = null; + RollbackListWidget.this.screen.closeAndReload(); + } + + public void delete() {} + } + + @Environment(EnvType.CLIENT) + public final class RollbackEntry extends Entry { + private final int backupNumber; + private final RollbackBackup backup; + + public RollbackEntry(int backupNumber, RollbackBackup rollbackBackup) { + super(); + this.backupNumber = backupNumber; + this.backup = rollbackBackup; + this.iconLocation = new Identifier("rollback", "backup/" + this.backupNumber + "/icon.png"); + this.icon = getIconTexture(); + } + + public Text getNarration() { + return Text.translatable( + "rollback.narrator.selectRollback", + this.backupNumber, + this.backup.getDateAsString(), + this.backup.daysPlayed + ); + } + + public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, + int mouseX, int mouseY, boolean hovered, float tickDelta) { + super.render( + Text.translatable("rollback.day", this.backup.daysPlayed), + Text.translatable("rollback.created", this.backup.getDateAsString()), + matrices, y, x, mouseX, hovered + ); + } + + private NativeImageBackedTexture getIconTexture() { if (this.backup.iconPath == null) { this.client.getTextureManager().destroyTexture(this.iconLocation); return null; @@ -204,10 +297,5 @@ public void delete() { Text.translatable("rollback.screen.cancel") )); } - - public void close() { - if (this.icon != null) - this.icon.close(); - } } } diff --git a/src/main/java/ir/mehradn/rollback/gui/RollbackScreen.java b/src/main/java/ir/mehradn/rollback/gui/RollbackScreen.java index d00eb50..74ca797 100644 --- a/src/main/java/ir/mehradn/rollback/gui/RollbackScreen.java +++ b/src/main/java/ir/mehradn/rollback/gui/RollbackScreen.java @@ -41,11 +41,11 @@ protected void init() { this.rollbackButton = addDrawableChild(ButtonWidget.builder( Text.translatable("rollback.screen.rollbackButton"), - (button) -> this.rollbackList.getSelectedAsOptional().ifPresent(RollbackListWidget.RollbackEntry::play) + (button) -> this.rollbackList.getSelectedAsOptional().ifPresent(RollbackListWidget.Entry::play) ).dimensions(this.width / 2 - 154, this.height - 52, 140, 20).build()); this.deleteButton = addDrawableChild(ButtonWidget.builder( Text.translatable("rollback.screen.delete"), - (button) -> this.rollbackList.getSelectedAsOptional().ifPresent(RollbackListWidget.RollbackEntry::delete) + (button) -> this.rollbackList.getSelectedAsOptional().ifPresent(RollbackListWidget.Entry::delete) ).dimensions(this.width / 2 - 10, this.height - 52, 80, 20).build()); addDrawableChild(ButtonWidget.builder( Text.translatable("rollback.screen.cancel"), @@ -71,7 +71,7 @@ protected void init() { (button) -> this.client.setScreen(MidnightConfig.getScreen(this, "rollback")) )); - setEntrySelected(false); + setEntrySelected(false, false); } public void closeAndReload() { @@ -85,13 +85,13 @@ public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { super.render(matrices, mouseX, mouseY, delta); } - public void setEntrySelected(boolean entrySelected) { - this.rollbackButton.active = entrySelected; - this.deleteButton.active = entrySelected; + public void setEntrySelected(boolean playActive, boolean deleteActice) { + this.rollbackButton.active = playActive; + this.deleteButton.active = deleteActice; } public void removed() { if (this.rollbackList != null) - this.rollbackList.children().forEach(RollbackListWidget.RollbackEntry::close); + this.rollbackList.children().forEach(RollbackListWidget.Entry::close); } } diff --git a/src/main/resources/assets/rollback/lang/en_us.json b/src/main/resources/assets/rollback/lang/en_us.json index 9462a38..ad48e58 100644 --- a/src/main/resources/assets/rollback/lang/en_us.json +++ b/src/main/resources/assets/rollback/lang/en_us.json @@ -14,10 +14,13 @@ "rollback.screen.openFolder": "Open Backups Folder", "rollback.screen.delete": "Delete", "rollback.screen.cancel": "Cancel", + "rollback.screen.currentSave": "Current Save", + "rollback.screen.lastPlayed": "Last Played: %s", "rollback.screen.deleteQuestion": "Are you sure you want to delete this backup?", "rollback.screen.rollbackQuestion": "Are you sure you want to rollback to this backup?", "rollback.screen.rollbackWarning": "Any not backed up progress will be lost!", "rollback.narrator.selectRollback": "Selected backup %d, created: %s, day: %d", + "rollback.narrator.selectCurrentSave": "Current save, last played: %s", "rollback.command.list.title": "Backups:", "rollback.command.list.noBackups": "There are no backups available for this world.", "rollback.command.unavailable": "This command is only available to server host.",