Fixed save file crash, implemented save ID mapping

This commit is contained in:
Anuken
2018-01-03 11:28:07 -05:00
parent 9e25bacfdd
commit d68768e24a
10 changed files with 538 additions and 27 deletions

View File

@@ -46,4 +46,16 @@ public abstract class SaveFileVersion {
public abstract void read(DataInputStream stream) throws IOException;
public abstract void write(DataOutputStream stream) throws IOException;
public static void writeString(DataOutputStream stream, String string) throws IOException{
stream.writeByte(string.length());
stream.writeBytes(string);
}
public static String readString(DataInputStream stream) throws IOException{
int length = stream.readByte();
byte[] result = new byte[length];
stream.read(result);
return new String(result);
}
}

View File

@@ -1,22 +1,31 @@
package io.anuke.mindustry.io;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Base64Coder;
import com.badlogic.gdx.utils.OrderedMap;
import com.badlogic.gdx.utils.IntMap;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.io.versions.Save12;
import io.anuke.mindustry.io.versions.Save13;
import io.anuke.mindustry.io.versions.Save14;
import io.anuke.ucore.UCore;
import io.anuke.ucore.core.Settings;
import java.io.*;
public class SaveIO{
public static final IntMap<SaveFileVersion> versions = new IntMap<>();
public static final Array<SaveFileVersion> versionArray = Array.with(
new Save12(),
new Save13(),
new Save14()
);
public static final OrderedMap<Integer, SaveFileVersion> versions = new OrderedMap(){{
put(12, new Save12());
put(13, new Save13());
}};
static{
for(SaveFileVersion version : versionArray){
versions.put(version.version, version);
}
}
public static void saveToSlot(int slot){
if(Vars.gwt){
@@ -26,11 +35,12 @@ public class SaveIO{
Settings.save();
}else{
FileHandle file = fileFor(slot);
file.moveTo(file.sibling(file.name() + "-backup." + file.extension()));
boolean exists = file.exists();
if(exists) file.moveTo(file.sibling(file.name() + "-backup." + file.extension()));
try {
write(fileFor(slot));
}catch (Exception e){
file.sibling(file.name() + "-backup." + file.extension()).moveTo(file);
if(exists) file.sibling(file.name() + "-backup." + file.extension()).moveTo(file);
throw new RuntimeException(e);
}
}
@@ -156,6 +166,6 @@ public class SaveIO{
}
public static SaveFileVersion getVersion(){
return versions.get(versions.orderedKeys().peek());
return versionArray.peek();
}
}

View File

@@ -82,6 +82,7 @@ public class Saves {
saves.add(slot);
SaveIO.saveToSlot(slot.index);
slot.meta = SaveIO.getData(slot.index);
current = slot;
}
public Array<SaveSlot> getSaveSlots(){

View File

@@ -8,7 +8,7 @@ import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.io.SaveFileVersion;
import io.anuke.mindustry.resource.Item;
import io.anuke.mindustry.resource.Weapon;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.BlockLoader;
import io.anuke.mindustry.world.GameMode;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.Blocks;
@@ -149,7 +149,7 @@ public class Save12 extends SaveFileVersion {
int blockid = stream.readInt();
Tile tile = Vars.world.tile(pos % Vars.world.width(), pos / Vars.world.width());
tile.setBlock(Block.getByID(blockid));
tile.setBlock(BlockLoader.getByOldID(blockid));
tile.link = link;
if(hasEntity){

View File

@@ -8,10 +8,7 @@ import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.io.SaveFileVersion;
import io.anuke.mindustry.resource.Item;
import io.anuke.mindustry.resource.Weapon;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.GameMode;
import io.anuke.mindustry.world.Generator;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.blocks.Blocks;
import io.anuke.mindustry.world.blocks.types.BlockPart;
import io.anuke.mindustry.world.blocks.types.Rock;
@@ -160,7 +157,7 @@ public class Save13 extends SaveFileVersion {
int blockid = stream.readInt();
Tile tile = Vars.world.tile(pos % Vars.world.width(), pos / Vars.world.width());
tile.setBlock(Block.getByID(blockid));
tile.setBlock(BlockLoader.getByOldID(blockid));
if(blockid == Blocks.blockpart.id){
tile.link = stream.readByte();

View File

@@ -0,0 +1,357 @@
package io.anuke.mindustry.io.versions;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.IntMap;
import com.badlogic.gdx.utils.TimeUtils;
import com.badlogic.gdx.utils.reflect.ClassReflection;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.io.SaveFileVersion;
import io.anuke.mindustry.resource.Item;
import io.anuke.mindustry.resource.Weapon;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.GameMode;
import io.anuke.mindustry.world.Generator;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.Blocks;
import io.anuke.mindustry.world.blocks.types.BlockPart;
import io.anuke.mindustry.world.blocks.types.Rock;
import io.anuke.ucore.core.Core;
import io.anuke.ucore.entities.Entities;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
import static io.anuke.mindustry.Vars.android;
public class Save14 extends SaveFileVersion{
public Save14(){
super(14);
}
@Override
public void read(DataInputStream stream) throws IOException {
int version = stream.readInt();
/*long loadTime = */stream.readLong();
if(version != this.version){
throw new RuntimeException("Save file version mismatch!");
}
//general state
byte mode = stream.readByte();
byte mapid = stream.readByte();
int wave = stream.readInt();
float wavetime = stream.readFloat();
//block header
int blocksize = stream.readInt();
IntMap<Block> map = new IntMap<>();
for(int i = 0; i < blocksize; i ++){
String name = readString(stream);
int id = stream.readShort();
map.put(id, Block.getByName(name));
}
float playerx = stream.readFloat();
float playery = stream.readFloat();
int playerhealth = stream.readInt();
Vars.player.x = playerx;
Vars.player.y = playery;
Vars.player.health = playerhealth;
Vars.control.setMode(GameMode.values()[mode]);
Core.camera.position.set(playerx, playery, 0);
//weapons
Vars.control.getWeapons().clear();
Vars.control.getWeapons().add(Weapon.blaster);
Vars.player.weapon = Weapon.blaster;
int weapons = stream.readByte();
for(int i = 0; i < weapons; i ++){
Vars.control.addWeapon(Weapon.values()[stream.readByte()]);
}
Vars.ui.updateWeapons();
//inventory
int totalItems = stream.readByte();
Arrays.fill(Vars.control.getItems(), 0);
for(int i = 0; i < totalItems; i ++){
Item item = Item.getByID(stream.readByte());
int amount = stream.readInt();
Vars.control.getItems()[item.id] = amount;
}
Vars.ui.updateItems();
//enemies
Entities.clear();
int enemies = stream.readInt();
Array<Enemy> enemiesToUpdate = new Array<>();
for(int i = 0; i < enemies; i ++){
byte type = stream.readByte();
int lane = stream.readByte();
float x = stream.readFloat();
float y = stream.readFloat();
byte tier = stream.readByte();
int health = stream.readShort();
try{
Enemy enemy = ClassReflection.newInstance(enemyIDs.get(type));
enemy.lane = lane;
enemy.health = health;
enemy.x = x;
enemy.y = y;
enemy.tier = tier;
enemy.add(Vars.control.enemyGroup);
enemiesToUpdate.add(enemy);
}catch (Exception e){
throw new RuntimeException(e);
}
}
Vars.control.setWaveData(enemies, wave, wavetime);
if(!android)
Vars.player.add();
//map
int seed = stream.readInt();
Vars.world.loadMap(Vars.world.maps().getMap(mapid), seed);
Vars.renderer.clearTiles();
for(Enemy enemy : enemiesToUpdate){
enemy.node = -2;
}
int rocks = stream.readInt();
for(int x = 0; x < Vars.world.width(); x ++){
for(int y = 0; y < Vars.world.height(); y ++){
Tile tile = Vars.world.tile(x, y);
//remove breakables like rocks
if(tile.breakable()){
Vars.world.tile(x, y).setBlock(Blocks.air);
}
}
}
for(int i = 0; i < rocks; i ++){
int pos = stream.readInt();
Tile tile = Vars.world.tile(pos % Vars.world.width(), pos / Vars.world.width());
Block result = Generator.rocks.get(tile.floor());
if(result != null) tile.setBlock(result);
}
int tiles = stream.readInt();
for(int i = 0; i < tiles; i ++){
int pos = stream.readInt();
int blockid = stream.readInt();
Tile tile = Vars.world.tile(pos % Vars.world.width(), pos / Vars.world.width());
tile.setBlock(map.get(blockid));
if(blockid == Blocks.blockpart.id){
tile.link = stream.readByte();
}
if(tile.entity != null){
byte rotation = stream.readByte();
short health = stream.readShort();
int items = stream.readByte();
tile.entity.health = health;
tile.setRotation(rotation);
for(int j = 0; j < items; j ++){
int itemid = stream.readByte();
int itemamount = stream.readInt();
tile.entity.items[itemid] = itemamount;
}
tile.entity.read(stream);
}
}
}
@Override
public void write(DataOutputStream stream) throws IOException {
//--META--
stream.writeInt(version); //version id
stream.writeLong(TimeUtils.millis()); //last saved
//--GENERAL STATE--
stream.writeByte(Vars.control.getMode().ordinal()); //gamemode
stream.writeByte(Vars.world.getMap().id); //map ID
stream.writeInt(Vars.control.getWave()); //wave
stream.writeFloat(Vars.control.getWaveCountdown()); //wave countdown
//--BLOCK HEADER--
stream.writeInt(Block.getAllBlocks().size);
for(int i = 0; i < Block.getAllBlocks().size; i ++){
Block block = Block.getAllBlocks().get(i);
writeString(stream, block.name);
stream.writeShort(block.id);
}
stream.writeFloat(Vars.player.x); //player x/y
stream.writeFloat(Vars.player.y);
stream.writeInt(Vars.player.health); //player health
stream.writeByte(Vars.control.getWeapons().size - 1); //amount of weapons
//start at 1, because the first weapon is always the starter - ignore that
for(int i = 1; i < Vars.control.getWeapons().size; i ++){
stream.writeByte(Vars.control.getWeapons().get(i).ordinal()); //weapon ordinal
}
//--INVENTORY--
int l = Vars.control.getItems().length;
int itemsize = 0;
for(int i = 0; i < l; i ++){
if(Vars.control.getItems()[i] > 0){
itemsize ++;
}
}
stream.writeByte(itemsize); //amount of items
for(int i = 0; i < l; i ++){
if(Vars.control.getItems()[i] > 0){
stream.writeByte(i); //item ID
stream.writeInt(Vars.control.getItems()[i]); //item amount
}
}
//--ENEMIES--
int totalEnemies = 0;
Array<Enemy> enemies = Vars.control.enemyGroup.all();
for(int i = 0; i < enemies.size; i ++){
Enemy enemy = enemies.get(i);
if(idEnemies.containsKey(enemy.getClass())){
totalEnemies ++;
}
}
stream.writeInt(totalEnemies); //enemy amount
for(int i = 0; i < enemies.size; i ++){
Enemy enemy = enemies.get(i);
if(idEnemies.containsKey(enemy.getClass())){
stream.writeByte(idEnemies.get(enemy.getClass())); //type
stream.writeByte(enemy.lane); //lane
stream.writeFloat(enemy.x); //x
stream.writeFloat(enemy.y); //y
stream.writeByte(enemy.tier); //tier
stream.writeShort(enemy.health); //health
}
}
//--MAP DATA--
//seed
stream.writeInt(Vars.world.getSeed());
int totalblocks = 0;
int totalrocks = 0;
for(int x = 0; x < Vars.world.width(); x ++){
for(int y = 0; y < Vars.world.height(); y ++){
Tile tile = Vars.world.tile(x, y);
if(tile.breakable()){
if(tile.block() instanceof Rock){
totalrocks ++;
}else{
totalblocks ++;
}
}
}
}
//amount of rocks
stream.writeInt(totalrocks);
//write all rocks
for(int x = 0; x < Vars.world.width(); x ++) {
for (int y = 0; y < Vars.world.height(); y++) {
Tile tile = Vars.world.tile(x, y);
if (tile.block() instanceof Rock) {
stream.writeInt(tile.packedPosition());
}
}
}
//write all blocks
stream.writeInt(totalblocks);
for(int x = 0; x < Vars.world.width(); x ++){
for(int y = 0; y < Vars.world.height(); y ++){
Tile tile = Vars.world.tile(x, y);
if(tile.breakable() && !(tile.block() instanceof Rock)){
stream.writeInt(x + y*Vars.world.width()); //tile pos
stream.writeInt(tile.block().id); //block ID
if(tile.block() instanceof BlockPart) stream.writeByte(tile.link);
if(tile.entity != null){
stream.writeByte(tile.getRotation()); //placerot
stream.writeShort(tile.entity.health); //health
byte amount = 0;
for(int i = 0; i < tile.entity.items.length; i ++){
if(tile.entity.items[i] > 0) amount ++;
}
stream.writeByte(amount); //amount of items
for(int i = 0; i < tile.entity.items.length; i ++){
if(tile.entity.items[i] > 0){
stream.writeByte(i); //item ID
stream.writeInt(tile.entity.items[i]); //item amount
}
}
tile.entity.write(stream);
}
}
}
}
}
}

View File

@@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectMap;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState;
import io.anuke.mindustry.core.GameState.State;
@@ -21,6 +22,7 @@ import io.anuke.ucore.util.Tmp;
public class Block{
private static int lastid;
private static Array<Block> blocks = new Array<Block>();
private static ObjectMap<String, Block> map = new ObjectMap<>();
protected static TextureRegion temp = new TextureRegion();
@@ -88,6 +90,11 @@ public class Block{
this.solid = false;
this.id = lastid++;
if(map.containsKey(name)){
throw new RuntimeException("Two blocks cannot have the same names! Problematic block: " + name);
}
map.put(name, this);
blocks.add(this);
}
@@ -258,6 +265,10 @@ public class Block{
public static Array<Block> getAllBlocks(){
return blocks;
}
public static Block getByName(String name){
return map.get(name);
}
public static Block getByID(int id){
return blocks.get(id);

View File

@@ -1,8 +1,109 @@
package io.anuke.mindustry.world;
import com.badlogic.gdx.utils.IntMap;
import com.badlogic.gdx.utils.ObjectIntMap;
import io.anuke.mindustry.world.blocks.*;
import io.anuke.ucore.UCore;
public class BlockLoader {
static final ObjectIntMap<String> defaultMap = map(
"air", 0,
"blockpart", 1,
"deepwater", 2,
"water", 3,
"lava", 4,
"oil", 5,
"stone", 6,
"blackstone", 7,
"iron", 8,
"coal", 9,
"titanium", 10,
"uranium", 11,
"dirt", 12,
"sand", 13,
"ice", 14,
"snow", 15,
"grass", 16,
"sandblock", 17,
"snowblock", 18,
"stoneblock", 19,
"blackstoneblock", 20,
"grassblock", 21,
"mossblock", 22,
"shrub", 23,
"rock", 24,
"icerock", 25,
"blackrock", 26,
"dirtblock", 27,
"stonewall", 28,
"ironwall", 29,
"steelwall", 30,
"titaniumwall", 31,
"duriumwall", 32,
"compositewall", 33,
"steelwall-large", 34,
"titaniumwall-large", 35,
"duriumwall-large", 36,
"titaniumshieldwall", 37,
"repairturret", 38,
"megarepairturret", 39,
"shieldgenerator", 40,
"door", 41,
"door-large", 42,
"conduit", 43,
"pulseconduit", 44,
"liquidrouter", 45,
"conveyor", 46,
"steelconveyor", 47,
"poweredconveyor", 48,
"router", 49,
"junction", 50,
"conveyortunnel", 51,
"liquidjunction", 52,
"liquiditemjunction", 53,
"powerbooster", 54,
"powerlaser", 55,
"powerlaserrouter", 56,
"powerlasercorner", 57,
"teleporter", 58,
"sorter", 59,
"core", 60,
"pump", 61,
"fluxpump", 62,
"smelter", 63,
"crucible", 64,
"coalpurifier", 65,
"titaniumpurifier", 66,
"oilrefinery", 67,
"stoneformer", 68,
"lavasmelter", 69,
"stonedrill", 70,
"irondrill", 71,
"coaldrill", 72,
"uraniumdrill", 73,
"titaniumdrill", 74,
"omnidrill", 75,
"coalgenerator", 76,
"thermalgenerator", 77,
"combustiongenerator", 78,
"rtgenerator", 79,
"nuclearreactor", 80,
"turret", 81,
"doubleturret", 82,
"machineturret", 83,
"shotgunturret", 84,
"flameturret", 85,
"sniperturret", 86,
"mortarturret", 87,
"laserturret", 88,
"waveturret", 89,
"plasmaturret", 90,
"chainturret", 91,
"titancannon", 92,
"playerspawn", 93,
"enemyspawn", 94
);
static final IntMap<Block> blockmap = new IntMap<>();
public static void load(){
@@ -15,5 +116,26 @@ public class BlockLoader {
SpecialBlocks.enemySpawn
//add any new block sections here
};
for(Block block : Block.getAllBlocks()){
UCore.log("\""+block.name+"\"", block.id, "");
}
for(String string : defaultMap.keys()){
Block block = Block.getByName(string);
blockmap.put(defaultMap.get(string, -1), block);
}
}
public static Block getByOldID(int id){
return blockmap.get(id);
}
private static ObjectIntMap<String> map(Object... objects){
ObjectIntMap<String> map = new ObjectIntMap<>();
for(int i = 0; i < objects.length/2; i ++){
map.put((String)objects[i*2], (int)objects[i*2+1]);
}
return map;
}
}

View File

@@ -102,6 +102,18 @@ public class ProductionBlocks{
craftEffect = Fx.purifystone;
}
},
siliconextractor = new LiquidCrafter("siliconextractor"){
{
input = Item.sand;
inputAmount = 5;
inputLiquid = Liquid.water;
liquidAmount = 18.99f;
output = Item.sand;
health = 50;
purifyTime = 50;
}
},
stonedrill = new Drill("stonedrill"){
{
@@ -207,16 +219,4 @@ public class ProductionBlocks{
breaktime *= 2.3f;
}
};
/*
siliconextractor = new LiquidCrafter("siliconextractor"){
{
input = Item.sand;
inputAmount = 5;
inputLiquid = Liquid.water;
liquidAmount = 18.99f;
output = Item.sand;
health = 50;
purifyTime = 50;
}
}*/;
}