I waste several hours
This commit is contained in:
@@ -72,10 +72,11 @@ public abstract class SaveFileReader{
|
||||
"slag", "molten-slag"
|
||||
);
|
||||
|
||||
protected final ReusableByteOutStream byteOutput = new ReusableByteOutStream();
|
||||
protected final DataOutputStream dataBytes = new DataOutputStream(byteOutput);
|
||||
protected final ReusableByteOutStream byteOutput = new ReusableByteOutStream(), byteOutput2 = new ReusableByteOutStream();
|
||||
protected final DataOutputStream dataBytes = new DataOutputStream(byteOutput), dataBytes2 = new DataOutputStream(byteOutput2);
|
||||
protected final ReusableByteOutStream byteOutputSmall = new ReusableByteOutStream();
|
||||
protected final DataOutputStream dataBytesSmall = new DataOutputStream(byteOutputSmall);
|
||||
protected boolean chunkNested = false;
|
||||
|
||||
protected int lastRegionLength;
|
||||
protected @Nullable CounterInputStream currCounter;
|
||||
@@ -112,23 +113,41 @@ public abstract class SaveFileReader{
|
||||
}
|
||||
|
||||
/** Write a chunk of input to the stream. An integer of some length is written first, followed by the data. */
|
||||
public void writeChunk(DataOutput output, boolean isByte, IORunner<DataOutput> runner) throws IOException{
|
||||
ReusableByteOutStream dout = isByte ? byteOutputSmall : byteOutput;
|
||||
//reset output position
|
||||
dout.reset();
|
||||
//write the needed info
|
||||
runner.accept(isByte ? dataBytesSmall : dataBytes);
|
||||
int length = dout.size();
|
||||
//write length (either int or byte) followed by the output bytes
|
||||
if(!isByte){
|
||||
output.writeInt(length);
|
||||
}else{
|
||||
if(length > 65535){
|
||||
throw new IOException("Byte write length exceeded: " + length + " > 65535");
|
||||
public void writeChunk(DataOutput output, boolean isShort, IORunner<DataOutput> runner) throws IOException{
|
||||
|
||||
//TODO awful
|
||||
boolean wasNested = chunkNested;
|
||||
if(!isShort){
|
||||
chunkNested = true;
|
||||
}
|
||||
ReusableByteOutStream dout =
|
||||
isShort ? byteOutputSmall :
|
||||
wasNested ? byteOutput2 :
|
||||
byteOutput;
|
||||
try{
|
||||
//reset output position
|
||||
dout.reset();
|
||||
//write the needed info
|
||||
runner.accept(
|
||||
isShort ? dataBytesSmall :
|
||||
wasNested ? dataBytes2 :
|
||||
dataBytes
|
||||
);
|
||||
|
||||
int length = dout.size();
|
||||
//write length (either int or byte) followed by the output bytes
|
||||
if(!isShort){
|
||||
output.writeInt(length);
|
||||
}else{
|
||||
if(length > 65535){
|
||||
throw new IOException("Byte write length exceeded: " + length + " > 65535");
|
||||
}
|
||||
output.writeShort(length);
|
||||
}
|
||||
output.writeShort(length);
|
||||
output.write(dout.getBytes(), 0, length);
|
||||
}finally{
|
||||
chunkNested = wasNested;
|
||||
}
|
||||
output.write(dout.getBytes(), 0, length);
|
||||
}
|
||||
|
||||
public int readChunk(DataInput input, IORunner<DataInput> runner) throws IOException{
|
||||
@@ -148,8 +167,8 @@ public abstract class SaveFileReader{
|
||||
}
|
||||
|
||||
/** Skip a chunk completely, discarding the bytes. */
|
||||
public void skipChunk(DataInput input, boolean isByte) throws IOException{
|
||||
int length = readChunk(input, isByte, t -> {});
|
||||
public void skipChunk(DataInput input, boolean isShort) throws IOException{
|
||||
int length = readChunk(input, isShort, t -> {});
|
||||
int skipped = input.skipBytes(length);
|
||||
if(length != skipped){
|
||||
throw new IOException("Could not skip bytes. Expected length: " + length + "; Actual length: " + skipped);
|
||||
@@ -180,4 +199,10 @@ public abstract class SaveFileReader{
|
||||
public interface IORunner<T>{
|
||||
void accept(T stream) throws IOException;
|
||||
}
|
||||
|
||||
public interface CustomChunk{
|
||||
void write(DataOutput stream) throws IOException;
|
||||
void read(DataInput stream) throws IOException;
|
||||
boolean shouldWrite();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
public static final Seq<SaveVersion> versionArray = Seq.with(new Save1(), new Save2(), new Save3(), new Save4(), new Save5(), new Save6(), new Save7());
|
||||
|
||||
static{
|
||||
for(SaveVersion version : versionArray){
|
||||
|
||||
@@ -23,7 +23,9 @@ import java.util.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public abstract class SaveVersion extends SaveFileReader{
|
||||
public int version;
|
||||
protected static OrderedMap<String, CustomChunk> customChunks = new OrderedMap<>();
|
||||
|
||||
public final int version;
|
||||
|
||||
//HACK stores the last read build of the save file, valid after read meta call
|
||||
protected int lastReadBuild;
|
||||
@@ -31,6 +33,14 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
//if null, fall back to EntityMapping's values
|
||||
protected @Nullable Prov[] entityMapping;
|
||||
|
||||
/**
|
||||
* Registers a custom save chunk reader/writer by name. This is mostly used for mods that need to save extra data.
|
||||
* @param name a mod-specific, unique name for identifying this chunk. Prefixing is recommended.
|
||||
* */
|
||||
public static void addCustomChunk(String name, CustomChunk chunk){
|
||||
customChunks.put(name, chunk);
|
||||
}
|
||||
|
||||
public SaveVersion(int version){
|
||||
this.version = version;
|
||||
}
|
||||
@@ -56,23 +66,49 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void read(DataInputStream stream, CounterInputStream counter, WorldContext context) throws IOException{
|
||||
public void read(DataInputStream stream, CounterInputStream counter, WorldContext context) throws IOException{
|
||||
region("meta", stream, counter, this::readMeta);
|
||||
region("content", stream, counter, this::readContentHeader);
|
||||
|
||||
try{
|
||||
region("map", stream, counter, in -> readMap(in, context));
|
||||
region("entities", stream, counter, this::readEntities);
|
||||
region("custom", stream, counter, this::readCustomChunks);
|
||||
}finally{
|
||||
content.setTemporaryMapper(null);
|
||||
}
|
||||
}
|
||||
|
||||
public final void write(DataOutputStream stream, StringMap extraTags) throws IOException{
|
||||
public void write(DataOutputStream stream, StringMap extraTags) throws IOException{
|
||||
region("meta", stream, out -> writeMeta(out, extraTags));
|
||||
region("content", stream, this::writeContentHeader);
|
||||
region("map", stream, this::writeMap);
|
||||
region("entities", stream, this::writeEntities);
|
||||
region("custom", stream, this::writeCustomChunks);
|
||||
}
|
||||
|
||||
public void writeCustomChunks(DataOutput stream) throws IOException{
|
||||
var chunks = customChunks.orderedKeys().select(s -> customChunks.get(s).shouldWrite());
|
||||
stream.writeInt(chunks.size);
|
||||
for(var chunkName : chunks){
|
||||
var chunk = customChunks.get(chunkName);
|
||||
stream.writeUTF(chunkName);
|
||||
|
||||
writeChunk(stream, false, chunk::write);
|
||||
}
|
||||
}
|
||||
|
||||
public void readCustomChunks(DataInput stream) throws IOException{
|
||||
int amount = stream.readInt();
|
||||
for(int i = 0; i < amount; i++){
|
||||
String name = stream.readUTF();
|
||||
var chunk = customChunks.get(name);
|
||||
if(chunk != null){
|
||||
readChunk(stream, false, chunk::read);
|
||||
}else{
|
||||
skipChunk(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void writeMeta(DataOutput stream, StringMap tags) throws IOException{
|
||||
|
||||
@@ -1,10 +1,31 @@
|
||||
package mindustry.io.versions;
|
||||
|
||||
import arc.util.io.*;
|
||||
import mindustry.io.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
/** This version does not read custom chunk data. */
|
||||
public class Save6 extends SaveVersion{
|
||||
|
||||
public Save6(){
|
||||
super(6);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInputStream stream, CounterInputStream counter, WorldContext context) throws IOException{
|
||||
region("meta", stream, counter, this::readMeta);
|
||||
region("content", stream, counter, this::readContentHeader);
|
||||
|
||||
try{
|
||||
region("map", stream, counter, in -> readMap(in, context));
|
||||
region("entities", stream, counter, this::readEntities);
|
||||
}finally{
|
||||
content.setTemporaryMapper(null);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
core/src/mindustry/io/versions/Save7.java
Normal file
10
core/src/mindustry/io/versions/Save7.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package mindustry.io.versions;
|
||||
|
||||
import mindustry.io.*;
|
||||
|
||||
public class Save7 extends SaveVersion{
|
||||
|
||||
public Save7(){
|
||||
super(7);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user