Files
Mindustry/core/src/mindustry/game/Saves.java
2022-04-13 17:38:18 -04:00

365 lines
9.9 KiB
Java

package mindustry.game;
import arc.*;
import arc.assets.*;
import arc.files.*;
import arc.graphics.*;
import arc.struct.*;
import arc.util.*;
import mindustry.*;
import mindustry.core.GameState.*;
import mindustry.game.EventType.*;
import mindustry.io.*;
import mindustry.io.SaveIO.*;
import mindustry.maps.Map;
import mindustry.type.*;
import java.io.*;
import java.text.*;
import java.util.*;
import static mindustry.Vars.*;
public class Saves{
Seq<SaveSlot> saves = new Seq<>();
@Nullable SaveSlot current;
private @Nullable SaveSlot lastSectorSave;
private boolean saving;
private float time;
long totalPlaytime;
private long lastTimestamp;
public Saves(){
Core.assets.setLoader(Texture.class, ".spreview", new SavePreviewLoader());
Events.on(StateChangeEvent.class, event -> {
if(event.to == State.menu){
totalPlaytime = 0;
lastTimestamp = 0;
current = null;
}
});
}
public void load(){
saves.clear();
for(Fi file : saveDirectory.list()){
if(!file.name().contains("backup") && SaveIO.isSaveValid(file)){
SaveSlot slot = new SaveSlot(file);
saves.add(slot);
slot.meta = SaveIO.getMeta(file);
}
}
//clear saves from build <130 that had the new naval sectors.
saves.removeAll(s -> {
if(s.getSector() != null && (s.getSector().id == 108 || s.getSector().id == 216) && s.meta.build <= 130 && s.meta.build > 0){
s.getSector().clearInfo();
s.file.delete();
return true;
}
return false;
});
lastSectorSave = saves.find(s -> s.isSector() && s.getName().equals(Core.settings.getString("last-sector-save", "<none>")));
//automatically assign sector save slots
for(SaveSlot slot : saves){
if(slot.getSector() != null){
if(slot.getSector().save != null){
Log.warn("Sector @ has two corresponding saves: @ and @", slot.getSector(), slot.getSector().save.file, slot.file);
}
slot.getSector().save = slot;
}
}
}
public @Nullable SaveSlot getLastSector(){
return lastSectorSave;
}
public @Nullable SaveSlot getCurrent(){
return current;
}
public void update(){
if(current != null && state.isGame()
&& !(state.isPaused() && Core.scene.hasDialog())){
if(lastTimestamp != 0){
totalPlaytime += Time.timeSinceMillis(lastTimestamp);
}
lastTimestamp = Time.millis();
}
if(state.isGame() && !state.gameOver && current != null && current.isAutosave()){
time += Time.delta;
if(time > Core.settings.getInt("saveinterval") * 60){
saving = true;
try{
current.save();
}catch(Throwable t){
Log.err(t);
}
Time.runTask(3f, () -> saving = false);
time = 0;
}
}else{
time = 0;
}
}
public long getTotalPlaytime(){
return totalPlaytime;
}
public void resetSave(){
current = null;
}
public boolean isSaving(){
return saving;
}
public Fi getSectorFile(Sector sector){
return saveDirectory.child("sector-" + sector.planet.name + "-" + sector.id + "." + saveExtension);
}
public void saveSector(Sector sector){
if(sector.save == null){
sector.save = new SaveSlot(getSectorFile(sector));
sector.save.setName(sector.save.file.nameWithoutExtension());
saves.add(sector.save);
}
sector.save.setAutosave(true);
sector.save.save();
lastSectorSave = sector.save;
Core.settings.put("last-sector-save", sector.save.getName());
}
public SaveSlot addSave(String name){
SaveSlot slot = new SaveSlot(getNextSlotFile());
slot.setName(name);
saves.add(slot);
slot.save();
return slot;
}
public SaveSlot importSave(Fi file) throws IOException{
SaveSlot slot = new SaveSlot(getNextSlotFile());
slot.importFile(file);
slot.setName(file.nameWithoutExtension());
saves.add(slot);
slot.meta = SaveIO.getMeta(slot.file);
current = slot;
return slot;
}
public Fi getNextSlotFile(){
int i = 0;
Fi file;
while((file = saveDirectory.child(i + "." + saveExtension)).exists()){
i ++;
}
return file;
}
public Seq<SaveSlot> getSaveSlots(){
return saves;
}
public void deleteAll(){
for(SaveSlot slot : saves.copy()){
if(!slot.isSector()){
slot.delete();
}
}
}
public class SaveSlot{
public final Fi file;
boolean requestedPreview;
public SaveMeta meta;
public SaveSlot(Fi file){
this.file = file;
}
public void load() throws SaveException{
try{
SaveIO.load(file);
meta = SaveIO.getMeta(file);
current = this;
totalPlaytime = meta.timePlayed;
savePreview();
}catch(Throwable e){
throw new SaveException(e);
}
}
public void save(){
long time = totalPlaytime;
long prev = totalPlaytime;
totalPlaytime = time;
SaveIO.save(file);
meta = SaveIO.getMeta(file);
if(state.isGame()){
current = this;
}
totalPlaytime = prev;
savePreview();
}
private void savePreview(){
if(Core.assets.isLoaded(loadPreviewFile().path())){
Core.assets.unload(loadPreviewFile().path());
}
mainExecutor.submit(() -> {
try{
previewFile().writePng(renderer.minimap.getPixmap());
requestedPreview = false;
}catch(Throwable t){
Log.err(t);
}
});
}
public Texture previewTexture(){
if(!previewFile().exists()){
return null;
}else if(Core.assets.isLoaded(loadPreviewFile().path())){
return Core.assets.get(loadPreviewFile().path());
}else if(!requestedPreview){
Core.assets.load(new AssetDescriptor<>(loadPreviewFile(), Texture.class));
requestedPreview = true;
}
return null;
}
private String index(){
return file.nameWithoutExtension();
}
private Fi previewFile(){
return mapPreviewDirectory.child("save_slot_" + index() + ".png");
}
private Fi loadPreviewFile(){
return previewFile().sibling(previewFile().name() + ".spreview");
}
public boolean isHidden(){
return isSector();
}
public String getPlayTime(){
return Strings.formatMillis(current == this ? totalPlaytime : meta.timePlayed);
}
public long getTimestamp(){
return meta.timestamp;
}
public String getDate(){
return SimpleDateFormat.getDateTimeInstance().format(new Date(meta.timestamp));
}
public Map getMap(){
return meta.map;
}
public void cautiousLoad(Runnable run){
Seq<String> mods = Seq.with(getMods());
mods.removeAll(Vars.mods.getModStrings());
if(!mods.isEmpty()){
ui.showConfirm("@warning", Core.bundle.format("mod.missing", mods.toString("\n")), run);
}else{
run.run();
}
}
public String getName(){
return Core.settings.getString("save-" + index() + "-name", "untitled");
}
public void setName(String name){
Core.settings.put("save-" + index() + "-name", name);
}
public String[] getMods(){
return meta.mods;
}
public @Nullable Sector getSector(){
return meta == null || meta.rules == null ? null : meta.rules.sector;
}
public boolean isSector(){
return getSector() != null;
}
public Gamemode mode(){
return meta.rules.mode();
}
public int getBuild(){
return meta.build;
}
public int getWave(){
return meta.wave;
}
public boolean isAutosave(){
return Core.settings.getBool("save-" + index() + "-autosave", true);
}
public void setAutosave(boolean save){
Core.settings.put("save-" + index() + "-autosave", save);
}
public void importFile(Fi from) throws IOException{
try{
from.copyTo(file);
if(previewFile().exists()){
requestedPreview = false;
previewFile().delete();
}
}catch(Exception e){
throw new IOException(e);
}
}
public void exportFile(Fi to) throws IOException{
try{
file.copyTo(to);
}catch(Exception e){
throw new IOException(e);
}
}
public void delete(){
if(SaveIO.backupFileFor(file).exists()){
SaveIO.backupFileFor(file).delete();
}
file.delete();
saves.remove(this, true);
if(this == current){
current = null;
}
if(Core.assets.isLoaded(loadPreviewFile().path())){
Core.assets.unload(loadPreviewFile().path());
}
}
}
}