Bugfixes / Rework of save discovery system

This commit is contained in:
Anuken
2019-10-27 14:58:26 -04:00
parent d969741f90
commit e103d62b75
12 changed files with 99 additions and 117 deletions

View File

@@ -230,7 +230,7 @@ data.export = Export Data
data.import = Import Data
data.exported = Data exported.
data.invalid = This isn't valid game data.
data.import.confirm = Importing external data will erase[scarlet] all[] your current game data.\n[accent]This cannot be undone![]\n\nOnce the data is imported, your game will exit immediately.
data.import.confirm = Importing external data will overwrite[scarlet] all[] your current game data.\n[accent]This cannot be undone![]\n\nOnce the data is imported, your game will exit immediately.
classic.export = Export Classic Data
classic.export.text = [accent]Mindustry[] has just had a major update.\nClassic (v3.5 build 40) save or map data has been detected. Would you like to export these saves to your phone's home folder, for use in the Mindustry Classic app?
quit.confirm = Are you sure you want to quit?

View File

@@ -185,6 +185,10 @@ public class World{
Events.fire(new WorldLoadEvent());
}
public void setGenerating(boolean gen){
this.generating = gen;
}
public boolean isGenerating(){
return generating;
}

View File

@@ -357,8 +357,8 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
public void drawOver(){
if(dead) return;
if(isBuilding()){
if(!state.isPaused() && isBuilding){
if(isBuilding() && isBuilding){
if(!state.isPaused()){
drawBuilding();
}
}else{
@@ -458,7 +458,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
}
//mine only when not building
if(buildRequest() == null){
if(buildRequest() == null || !isBuilding){
updateMining();
}
}

View File

@@ -66,14 +66,8 @@ public class GlobalData{
throw new IllegalArgumentException("Not valid save data.");
}
//purge existing data
for(FileHandle f : base.list()){
if(f.isDirectory()){
f.deleteDirectory();
}else if(!f.name().equals("zipdata.zip")){
f.delete();
}
}
//purge existing tmp data, keep everything else
tmpDirectory.deleteDirectory();
zipped.walk(f -> f.copyTo(base.child(f.path())));
dest.delete();

View File

@@ -22,13 +22,12 @@ import java.util.*;
import static io.anuke.mindustry.Vars.*;
public class Saves{
private int nextSlot;
private Array<SaveSlot> saves = new Array<>();
private IntMap<SaveSlot> saveMap = new IntMap<>();
private SaveSlot current;
private AsyncExecutor previewExecutor = new AsyncExecutor(1);
private boolean saving;
private float time;
private FileHandle zoneFile;
private long totalPlaytime;
private long lastTimestamp;
@@ -47,16 +46,13 @@ public class Saves{
public void load(){
saves.clear();
IntArray slots = Core.settings.getObject("save-slots", IntArray.class, IntArray::new);
zoneFile = saveDirectory.child("-1.msav");
for(int i = 0; i < slots.size; i++){
int index = slots.get(i);
if(SaveIO.isSaveValid(index)){
SaveSlot slot = new SaveSlot(index);
for(FileHandle file : saveDirectory.list()){
if(!file.name().contains("backup") && SaveIO.isSaveValid(file)){
SaveSlot slot = new SaveSlot(file);
saves.add(slot);
saveMap.put(slot.index, slot);
slot.meta = SaveIO.getMeta(index);
nextSlot = Math.max(index + 1, nextSlot);
slot.meta = SaveIO.getMeta(file);
}
}
}
@@ -110,73 +106,63 @@ public class Saves{
}
public void zoneSave(){
SaveSlot slot = new SaveSlot(-1);
SaveSlot slot = new SaveSlot(zoneFile);
slot.setName("zone");
saves.remove(s -> s.index == -1);
saves.remove(s -> s.file.equals(zoneFile));
saves.add(slot);
saveMap.put(slot.index, slot);
slot.save();
saveSlots();
}
public SaveSlot addSave(String name){
SaveSlot slot = new SaveSlot(nextSlot);
nextSlot++;
SaveSlot slot = new SaveSlot(getNextSlotFile());
slot.setName(name);
saves.add(slot);
saveMap.put(slot.index, slot);
slot.save();
saveSlots();
return slot;
}
public SaveSlot importSave(FileHandle file) throws IOException{
SaveSlot slot = new SaveSlot(nextSlot);
SaveSlot slot = new SaveSlot(getNextSlotFile());
slot.importFile(file);
nextSlot++;
slot.setName(file.nameWithoutExtension());
saves.add(slot);
saveMap.put(slot.index, slot);
slot.meta = SaveIO.getMeta(slot.index);
slot.meta = SaveIO.getMeta(slot.file);
current = slot;
saveSlots();
return slot;
}
public SaveSlot getZoneSlot(){
SaveSlot slot = getByID(-1);
SaveSlot slot = getSaveSlots().find(s -> s.file.equals(zoneFile));
return slot == null || slot.getZone() == null ? null : slot;
}
public SaveSlot getByID(int id){
return saveMap.get(id);
public FileHandle getNextSlotFile(){
int i = 0;
FileHandle file;
while((file = saveDirectory.child(i + "." + saveExtension)).exists()){
i ++;
}
return file;
}
public Array<SaveSlot> getSaveSlots(){
return saves;
}
private void saveSlots(){
IntArray result = new IntArray(saves.size);
for(int i = 0; i < saves.size; i++) result.add(saves.get(i).index);
Core.settings.putObject("save-slots", result);
Core.settings.save();
}
public class SaveSlot{
public final int index;
//public final int index;
public final FileHandle file;
boolean requestedPreview;
SaveMeta meta;
public SaveSlot(int index){
this.index = index;
public SaveSlot(FileHandle file){
this.file = file;
}
public void load() throws SaveException{
try{
SaveIO.loadFromSlot(index);
meta = SaveIO.getMeta(index);
SaveIO.load(file);
meta = SaveIO.getMeta(file);
current = this;
totalPlaytime = meta.timePlayed;
savePreview();
@@ -190,8 +176,8 @@ public class Saves{
long prev = totalPlaytime;
totalPlaytime = time;
SaveIO.saveToSlot(index);
meta = SaveIO.getMeta(index);
SaveIO.save(file);
meta = SaveIO.getMeta(file);
if(!state.is(State.menu)){
current = this;
}
@@ -226,8 +212,12 @@ public class Saves{
return null;
}
private String index(){
return file.nameWithoutExtension();
}
private FileHandle previewFile(){
return mapPreviewDirectory.child("save_slot_" + index + ".png");
return mapPreviewDirectory.child("save_slot_" + index() + ".png");
}
private FileHandle loadPreviewFile(){
@@ -266,11 +256,11 @@ public class Saves{
}
public String getName(){
return Core.settings.getString("save-" + index + "-name", "untitled");
return Core.settings.getString("save-" + index() + "-name", "untitled");
}
public void setName(String name){
Core.settings.put("save-" + index + "-name", name);
Core.settings.put("save-" + index() + "-name", name);
Core.settings.save();
}
@@ -295,34 +285,33 @@ public class Saves{
}
public boolean isAutosave(){
return Core.settings.getBool("save-" + index + "-autosave", true);
return Core.settings.getBool("save-" + index() + "-autosave", true);
}
public void setAutosave(boolean save){
Core.settings.put("save-" + index + "-autosave", save);
Core.settings.put("save-" + index() + "-autosave", save);
Core.settings.save();
}
public void importFile(FileHandle file) throws IOException{
public void importFile(FileHandle from) throws IOException{
try{
file.copyTo(SaveIO.fileFor(index));
from.copyTo(file);
}catch(Exception e){
throw new IOException(e);
}
}
public void exportFile(FileHandle file) throws IOException{
public void exportFile(FileHandle to) throws IOException{
try{
SaveIO.fileFor(index).copyTo(file);
file.copyTo(to);
}catch(Exception e){
throw new IOException(e);
}
}
public void delete(){
SaveIO.fileFor(index).delete();
file.delete();
saves.removeValue(this, true);
saveMap.remove(index);
if(this == current){
current = null;
}
@@ -330,8 +319,6 @@ public class Saves{
if(Core.assets.isLoaded(loadPreviewFile().path())){
Core.assets.unload(loadPreviewFile().path());
}
saveSlots();
}
}
}

View File

@@ -19,6 +19,7 @@ import io.anuke.mindustry.input.*;
import io.anuke.mindustry.input.PlaceUtils.*;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.blocks.*;
import java.io.*;
import java.util.zip.*;
@@ -205,7 +206,7 @@ public class Schematics implements Loadable{
/** Creates an array of build requests from a schematic's data, centered on the provided x+y coordinates. */
public Array<BuildRequest> toRequests(Schematic schem, int x, int y){
return schem.tiles.map(t -> new BuildRequest(t.x + x - schem.width/2, t.y + y - schem.height/2, t.rotation, t.block).original(t.x, t.y, schem.width, schem.height).configure(t.config)).removeAll(s -> !s.block.isVisible());
return schem.tiles.map(t -> new BuildRequest(t.x + x - schem.width/2, t.y + y - schem.height/2, t.rotation, t.block).original(t.x, t.y, schem.width, schem.height).configure(t.config)).removeAll(s -> !s.block.isVisible() || !s.block.unlocked());
}
/** Adds a schematic to the list, also copying it into the files.*/
@@ -251,7 +252,7 @@ public class Schematics implements Loadable{
for(int cy = y; cy <= y2; cy++){
Tile linked = world.ltile(cx, cy);
if(linked != null && linked.entity != null && linked.entity.block.isVisible()){
if(linked != null && linked.entity != null && linked.entity.block.isVisible() && !(linked.block() instanceof BuildBlock)){
int top = linked.block().size/2;
int bot = linked.block().size % 2 == 1 ? -linked.block().size/2 : -(linked.block().size - 1)/2;
minx = Math.min(linked.x + bot, minx);
@@ -279,7 +280,7 @@ public class Schematics implements Loadable{
for(int cy = oy; cy <= oy2; cy++){
Tile tile = world.ltile(cx, cy);
if(tile != null && tile.entity != null && !counted.contains(tile.pos())){
if(tile != null && tile.entity != null && !counted.contains(tile.pos()) && !(tile.block() instanceof BuildBlock) && tile.entity.block.isVisible()){
int config = tile.entity.config();
if(tile.block().posConfig){
config = Pos.get(Pos.x(config) + offsetX, Pos.y(config) + offsetY);

View File

@@ -182,7 +182,7 @@ public class DesktopInput extends InputHandler{
mode = none;
}
if(mode != none || isPlacing()){
if(mode == placing || isPlacing()){
selectRequests.clear();
lastSchematic = null;
}
@@ -379,7 +379,7 @@ public class DesktopInput extends InputHandler{
deleting = true;
}else if(selected != null){
//only begin shooting if there's no cursor event
if(!tileTapped(selected) && !tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && player.buildQueue().size == 0 && !droppingItem &&
if(!tileTapped(selected) && !tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && (player.buildQueue().size == 0 || !player.isBuilding) && !droppingItem &&
!tryBeginMine(selected) && player.getMineTile() == null && !ui.chatfrag.chatOpen()){
player.isShooting = true;
}

View File

@@ -34,37 +34,23 @@ public class SaveIO{
return versions.get(version);
}
public static void saveToSlot(int slot){
FileHandle file = fileFor(slot);
public static void save(FileHandle file){
boolean exists = file.exists();
if(exists) file.moveTo(backupFileFor(file));
try{
write(fileFor(slot));
write(file);
}catch(Exception e){
if(exists) backupFileFor(file).moveTo(file);
throw new RuntimeException(e);
}
}
public static void loadFromSlot(int slot) throws SaveException{
load(fileFor(slot));
public static DataInputStream getStream(FileHandle file){
return new DataInputStream(new InflaterInputStream(file.read(bufferSize)));
}
public static DataInputStream getSlotStream(int slot){
return new DataInputStream(new InflaterInputStream(fileFor(slot).read(bufferSize)));
}
public static DataInputStream getBackupSlotStream(int slot){
return new DataInputStream(new InflaterInputStream(backupFileFor(fileFor(slot)).read(bufferSize)));
}
public static boolean isSaveValid(int slot){
try{
getMeta(slot);
return true;
}catch(Exception e){
return false;
}
public static DataInputStream getBackupStream(FileHandle file){
return new DataInputStream(new InflaterInputStream(backupFileFor(file).read(bufferSize)));
}
public static boolean isSaveValid(FileHandle file){
@@ -85,11 +71,11 @@ public class SaveIO{
}
}
public static SaveMeta getMeta(int slot){
public static SaveMeta getMeta(FileHandle file){
try{
return getMeta(getSlotStream(slot));
return getMeta(getStream(file));
}catch(Exception e){
return getMeta(getBackupSlotStream(slot));
return getMeta(getBackupStream(file));
}
}
@@ -167,6 +153,7 @@ public class SaveIO{
}catch(Exception e){
throw new SaveException(e);
}finally{
world.setGenerating(false);
content.setTemporaryMapper(null);
}
}

View File

@@ -3,6 +3,7 @@ package io.anuke.mindustry.io;
import io.anuke.arc.collection.*;
import io.anuke.arc.util.*;
import io.anuke.arc.util.io.*;
import io.anuke.mindustry.content.*;
import io.anuke.mindustry.core.*;
import io.anuke.mindustry.ctype.*;
import io.anuke.mindustry.entities.*;
@@ -166,6 +167,7 @@ public abstract class SaveVersion extends SaveFileReader{
short floorid = stream.readShort();
short oreid = stream.readShort();
int consecutives = stream.readUnsignedByte();
if(content.block(floorid) == Blocks.air) floorid = Blocks.stone.id;
context.create(x, y, floorid, oreid, (short)0);
@@ -182,6 +184,7 @@ public abstract class SaveVersion extends SaveFileReader{
int x = i % width, y = i / width;
Block block = content.block(stream.readShort());
Tile tile = context.tile(x, y);
if(block == null) block = Blocks.air;
tile.setBlock(block);
if(tile.entity != null){

View File

@@ -4,6 +4,7 @@ import io.anuke.arc.collection.*;
import io.anuke.arc.function.*;
import io.anuke.arc.math.*;
import io.anuke.arc.math.geom.*;
import io.anuke.arc.util.ArcAnnotate.*;
import io.anuke.mindustry.content.*;
import io.anuke.mindustry.entities.traits.*;
import io.anuke.mindustry.entities.type.*;
@@ -152,7 +153,7 @@ public class Tile implements Position, TargetTrait{
return team;
}
public void setBlock(Block type, Team team, int rotation){
public void setBlock(@NonNull Block type, Team team, int rotation){
preChanged();
this.block = type;
this.team = (byte)team.ordinal();
@@ -160,11 +161,12 @@ public class Tile implements Position, TargetTrait{
changed();
}
public void setBlock(Block type, Team team){
public void setBlock(@NonNull Block type, Team team){
setBlock(type, team, 0);
}
public void setBlock(Block type){
public void setBlock(@NonNull Block type){
if(type == null) throw new IllegalArgumentException("Block cannot be null.");
preChanged();
this.block = type;
this.rotation = 0;
@@ -172,13 +174,13 @@ public class Tile implements Position, TargetTrait{
}
/**This resets the overlay!*/
public void setFloor(Floor type){
public void setFloor(@NonNull Floor type){
this.floor = type;
this.overlay = (Floor)Blocks.air;
}
/** Sets the floor, preserving overlay.*/
public void setFloorUnder(Floor floor){
public void setFloorUnder(@NonNull Floor floor){
Block overlay = this.overlay;
setFloor(floor);
setOverlay(overlay);

View File

@@ -670,28 +670,25 @@ public class ServerControl implements ApplicationListener{
if(state.is(State.playing)){
err("Already hosting. Type 'stop' to stop hosting first.");
return;
}else if(!Strings.canParseInt(arg[0])){
err("Invalid save slot '{0}'.", arg[0]);
return;
}
int slot = Strings.parseInt(arg[0]);
FileHandle file = saveDirectory.child(arg[0] + "." + saveExtension);
if(!SaveIO.isSaveValid(slot)){
if(!SaveIO.isSaveValid(file)){
err("No (valid) save data found for slot.");
return;
}
Core.app.post(() -> {
try{
SaveIO.loadFromSlot(slot);
SaveIO.load(file);
state.rules.zone = null;
info("Save loaded.");
host();
state.set(State.playing);
}catch(Throwable t){
err("Failed to load save. Outdated or corrupt file.");
}
info("Save loaded.");
host();
state.set(State.playing);
});
});
@@ -699,18 +696,25 @@ public class ServerControl implements ApplicationListener{
if(!state.is(State.playing)){
err("Not hosting. Host a game first.");
return;
}else if(!Strings.canParseInt(arg[0])){
err("Invalid save slot '{0}'.", arg[0]);
return;
}
FileHandle file = saveDirectory.child(arg[0] + "." + saveExtension);
Core.app.post(() -> {
int slot = Strings.parseInt(arg[0]);
SaveIO.saveToSlot(slot);
info("Saved to slot {0}.", slot);
SaveIO.save(file);
info("Saved to {0}.", file);
});
});
handler.register("saves", "List all saves in the save directory.", arg -> {
info("Save files: ");
for(FileHandle file : saveDirectory.list()){
if(file.extension().equals(saveExtension)){
info("| &ly{0}", file.nameWithoutExtension());
}
}
});
handler.register("gameover", "Force a game over.", arg -> {
if(state.is(State.menu)){
info("Not playing a map.");

View File

@@ -198,7 +198,7 @@ public class ApplicationTests{
void save(){
world.loadMap(testMap);
assertTrue(state.teams.get(defaultTeam).cores.size > 0);
SaveIO.saveToSlot(0);
SaveIO.save(saveDirectory.child("0.msav"));
}
@Test
@@ -206,9 +206,9 @@ public class ApplicationTests{
world.loadMap(testMap);
Map map = world.getMap();
SaveIO.saveToSlot(0);
SaveIO.save(saveDirectory.child("0.msav"));
resetWorld();
SaveIO.loadFromSlot(0);
SaveIO.load(saveDirectory.child("0.msav"));
assertEquals(world.width(), map.width);
assertEquals(world.height(), map.height);