diff --git a/core/src/mindustry/io/SaveIO.java b/core/src/mindustry/io/SaveIO.java index dea4ce7b72..7f6bb42845 100644 --- a/core/src/mindustry/io/SaveIO.java +++ b/core/src/mindustry/io/SaveIO.java @@ -20,7 +20,7 @@ public class SaveIO{ /** Save format header. */ public static final byte[] header = {'M', 'S', 'A', 'V'}; public static final IntMap versions = new IntMap<>(); - public static final Seq versionArray = Seq.with(new Save1(), new Save2(), new Save3(), new Save4(), new Save5(), new Save6(), new Save7(), new Save8()); + public static final Seq versionArray = Seq.with(new Save1(), new Save2(), new Save3(), new Save4(), new Save5(), new Save6(), new Save7(), new Save8(), new Save9()); static{ for(SaveVersion version : versionArray){ diff --git a/core/src/mindustry/io/SaveVersion.java b/core/src/mindustry/io/SaveVersion.java index 70cb4b3f46..2efcd50070 100644 --- a/core/src/mindustry/io/SaveVersion.java +++ b/core/src/mindustry/io/SaveVersion.java @@ -235,11 +235,21 @@ public abstract class SaveVersion extends SaveFileReader{ boolean savedata = tile.floor().saveData || tile.overlay().saveData || tile.block().saveData; - byte packed = (byte)((tile.build != null ? 1 : 0) | (savedata ? 2 : 0)); + //in the old version, the second bit was set to indicate presence of data, but that approach was flawed - it didn't allow buildings + data on the same tile + //so now the third bit is used instead + byte packed = (byte)((tile.build != null ? 1 : 0) | (savedata ? 4 : 0)); - //make note of whether there was an entity/rotation here + //make note of whether there was an entity or custom tile data here stream.writeByte(packed); + if(savedata){ + //the new 'extra data' format writes 7 bytes of data instead of 1 + stream.writeByte(tile.data); + stream.writeByte(tile.floorData); + stream.writeByte(tile.overlayData); + stream.writeInt(tile.extraData); + } + //only write the entity for multiblocks once - in the center if(tile.build != null){ if(tile.isCenter()){ @@ -251,9 +261,7 @@ public abstract class SaveVersion extends SaveFileReader{ }else{ stream.writeBoolean(false); } - }else if(savedata){ - stream.writeByte(tile.data); - }else{ + }else if(!savedata){ //don't write consecutive blocks when there is custom data //write consecutive non-entity blocks int consecutives = 0; @@ -310,7 +318,16 @@ public abstract class SaveVersion extends SaveFileReader{ boolean isCenter = true; byte packedCheck = stream.readByte(); boolean hadEntity = (packedCheck & 1) != 0; - boolean hadData = (packedCheck & 2) != 0; + //old data format (bit 2): 1 byte only if no building is present + //new data format (bit 3): 7 bytes (3x block-specific bytes + 1x 4-byte extra data int) + boolean hadDataOld = (packedCheck & 2) != 0, hadDataNew = (packedCheck & 4) != 0; + + if(hadDataNew){ + tile.data = stream.readByte(); + tile.floorData = stream.readByte(); + tile.overlayData = stream.readByte(); + tile.extraData = stream.readInt(); + } if(hadEntity){ isCenter = stream.readBoolean(); @@ -339,9 +356,12 @@ public abstract class SaveVersion extends SaveFileReader{ context.onReadBuilding(); } - }else if(hadData){ + }else if(hadDataOld || hadDataNew){ //never read consecutive blocks if there's any kind of data tile.setBlock(block); - tile.data = stream.readByte(); + if(hadDataOld){ + //the old data format was only read in the case where there is no building, and only contained a single byte + tile.data = stream.readByte(); + } }else{ int consecutives = stream.readUnsignedByte(); diff --git a/core/src/mindustry/io/versions/Save8.java b/core/src/mindustry/io/versions/Save8.java index 8dc1c0ca01..b65984b044 100644 --- a/core/src/mindustry/io/versions/Save8.java +++ b/core/src/mindustry/io/versions/Save8.java @@ -2,6 +2,7 @@ package mindustry.io.versions; import mindustry.io.*; +/** Adds support for the marker binary data region. The code is unchanged here, because it was easier to add a >= 8 check in the SaveVersion class itself. */ public class Save8 extends SaveVersion{ public Save8(){ diff --git a/core/src/mindustry/io/versions/Save9.java b/core/src/mindustry/io/versions/Save9.java new file mode 100644 index 0000000000..08d1786e18 --- /dev/null +++ b/core/src/mindustry/io/versions/Save9.java @@ -0,0 +1,11 @@ +package mindustry.io.versions; + +import mindustry.io.*; + +/** Adds support for the new 7-byte custom tile data. This can read Save8 data, but Save8 doesn't know how to handle this version's output, thus the version change. */ +public class Save9 extends SaveVersion{ + + public Save9(){ + super(9); + } +} diff --git a/core/src/mindustry/world/Tile.java b/core/src/mindustry/world/Tile.java index 52e656e87d..ee5e19436f 100644 --- a/core/src/mindustry/world/Tile.java +++ b/core/src/mindustry/world/Tile.java @@ -26,8 +26,14 @@ public class Tile implements Position, QuadTreeObject, Displayable{ private static final TileFloorChangeEvent floorChange = new TileFloorChangeEvent(); private static final ObjectSet tileSet = new ObjectSet<>(); - /** Extra data for very specific blocks. */ - public byte data; + /** + * Extra data for specific blocks. Only saved if Block#saveData is true. + * It is generally recommended that blocks only access data in their own category unless necessary - for example, a floor should not read/write overlay data. + * However, one byte may sometimes not be enough to hold enough data, in which case "overlapping" data storage is necessary. + * */ + public byte data, floorData, overlayData; + /** Even more data for blocks. Use with caution; any floor/block can access this value. Due to 8-byte alignment of Java objects, this extra 4-byte field can be added with no additional cost.*/ + public int extraData; /** Tile entity, usually null. */ public @Nullable Building build; public short x, y;