Better tile custom data support

This commit is contained in:
Anuken
2025-07-12 17:28:39 -04:00
parent 5e6d599120
commit 0939076b4d
5 changed files with 49 additions and 11 deletions

View File

@@ -20,7 +20,7 @@ public class SaveIO{
/** Save format header. */
public static final byte[] header = {'M', 'S', 'A', 'V'};
public static final IntMap<SaveVersion> versions = new IntMap<>();
public static final Seq<SaveVersion> versionArray = Seq.with(new Save1(), new Save2(), new Save3(), new Save4(), new Save5(), new Save6(), new Save7(), new Save8());
public static final Seq<SaveVersion> 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){

View File

@@ -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();

View File

@@ -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(){

View File

@@ -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);
}
}

View File

@@ -26,8 +26,14 @@ public class Tile implements Position, QuadTreeObject, Displayable{
private static final TileFloorChangeEvent floorChange = new TileFloorChangeEvent();
private static final ObjectSet<Building> 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;