Save version chunks changed to 4-byte lengths
This commit is contained in:
@@ -61,6 +61,7 @@ public class NetClient implements ApplicationListener{
|
|||||||
/** Byte stream for reading in snapshots. */
|
/** Byte stream for reading in snapshots. */
|
||||||
private ReusableByteInStream byteStream = new ReusableByteInStream();
|
private ReusableByteInStream byteStream = new ReusableByteInStream();
|
||||||
private DataInputStream dataStream = new DataInputStream(byteStream);
|
private DataInputStream dataStream = new DataInputStream(byteStream);
|
||||||
|
private Reads dataStreamReads = new Reads(dataStream);
|
||||||
/** Packet handlers for custom types of messages. */
|
/** Packet handlers for custom types of messages. */
|
||||||
private ObjectMap<String, Seq<Cons<String>>> customPacketHandlers = new ObjectMap<>();
|
private ObjectMap<String, Seq<Cons<String>>> customPacketHandlers = new ObjectMap<>();
|
||||||
/** Packet handlers for custom types of messages, in binary. */
|
/** Packet handlers for custom types of messages, in binary. */
|
||||||
@@ -485,9 +486,10 @@ public class NetClient implements ApplicationListener{
|
|||||||
netClient.lastSnapshotTimestamp = Time.millis();
|
netClient.lastSnapshotTimestamp = Time.millis();
|
||||||
netClient.byteStream.setBytes(data);
|
netClient.byteStream.setBytes(data);
|
||||||
DataInputStream input = netClient.dataStream;
|
DataInputStream input = netClient.dataStream;
|
||||||
|
Reads reads = netClient.dataStreamReads;
|
||||||
|
|
||||||
for(int j = 0; j < amount; j++){
|
for(int j = 0; j < amount; j++){
|
||||||
readSyncEntity(input, Reads.get(input));
|
readSyncEntity(input, reads);
|
||||||
}
|
}
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
//don't disconnect, just log it
|
//don't disconnect, just log it
|
||||||
@@ -511,6 +513,7 @@ public class NetClient implements ApplicationListener{
|
|||||||
try{
|
try{
|
||||||
netClient.byteStream.setBytes(data);
|
netClient.byteStream.setBytes(data);
|
||||||
DataInputStream input = netClient.dataStream;
|
DataInputStream input = netClient.dataStream;
|
||||||
|
Reads reads = netClient.dataStreamReads;
|
||||||
|
|
||||||
for(int i = 0; i < amount; i++){
|
for(int i = 0; i < amount; i++){
|
||||||
int pos = input.readInt();
|
int pos = input.readInt();
|
||||||
@@ -524,7 +527,7 @@ public class NetClient implements ApplicationListener{
|
|||||||
Log.warn("Block ID mismatch at @: @ != @. Skipping block snapshot.", tile, tile.build.block.id, block);
|
Log.warn("Block ID mismatch at @: @ != @. Skipping block snapshot.", tile, tile.build.block.id, block);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tile.build.readSync(Reads.get(input), tile.build.version());
|
tile.build.readSync(reads, tile.build.version());
|
||||||
}
|
}
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Log.err(e);
|
Log.err(e);
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ public class NetServer implements ApplicationListener{
|
|||||||
private ReusableByteOutStream syncStream = new ReusableByteOutStream();
|
private ReusableByteOutStream syncStream = new ReusableByteOutStream();
|
||||||
/** Data stream for writing player sync data to. */
|
/** Data stream for writing player sync data to. */
|
||||||
private DataOutputStream dataStream = new DataOutputStream(syncStream);
|
private DataOutputStream dataStream = new DataOutputStream(syncStream);
|
||||||
|
private Writes dataStreamWrites = new Writes(dataStream);
|
||||||
/** Packet handlers for custom types of messages. */
|
/** Packet handlers for custom types of messages. */
|
||||||
private ObjectMap<String, Seq<Cons2<Player, String>>> customPacketHandlers = new ObjectMap<>();
|
private ObjectMap<String, Seq<Cons2<Player, String>>> customPacketHandlers = new ObjectMap<>();
|
||||||
/** Packet handlers for custom types of messages - binary version. */
|
/** Packet handlers for custom types of messages - binary version. */
|
||||||
@@ -938,7 +939,7 @@ public class NetServer implements ApplicationListener{
|
|||||||
|
|
||||||
dataStream.writeInt(build.pos());
|
dataStream.writeInt(build.pos());
|
||||||
dataStream.writeShort(build.block.id);
|
dataStream.writeShort(build.block.id);
|
||||||
build.writeSync(Writes.get(dataStream));
|
build.writeSync(dataStreamWrites);
|
||||||
|
|
||||||
if(syncStream.size() > maxSnapshotSize){
|
if(syncStream.size() > maxSnapshotSize){
|
||||||
dataStream.close();
|
dataStream.close();
|
||||||
@@ -993,7 +994,7 @@ public class NetServer implements ApplicationListener{
|
|||||||
dataStream.writeInt(entity.id()); //write id
|
dataStream.writeInt(entity.id()); //write id
|
||||||
dataStream.writeByte(entity.classId() & 0xFF); //write type ID
|
dataStream.writeByte(entity.classId() & 0xFF); //write type ID
|
||||||
entity.beforeWrite();
|
entity.beforeWrite();
|
||||||
entity.writeSync(Writes.get(dataStream)); //write entity
|
entity.writeSync(dataStreamWrites); //write entity itself
|
||||||
|
|
||||||
sent++;
|
sent++;
|
||||||
|
|
||||||
|
|||||||
@@ -593,11 +593,13 @@ public class Schematics implements Loadable{
|
|||||||
|
|
||||||
if(total > 128 * 128) throw new IOException("Invalid schematic: Too many blocks.");
|
if(total > 128 * 128) throw new IOException("Invalid schematic: Too many blocks.");
|
||||||
|
|
||||||
|
Reads read = new Reads(stream);
|
||||||
|
|
||||||
Seq<Stile> tiles = new Seq<>(total);
|
Seq<Stile> tiles = new Seq<>(total);
|
||||||
for(int i = 0; i < total; i++){
|
for(int i = 0; i < total; i++){
|
||||||
Block block = blocks.get(stream.readByte());
|
Block block = blocks.get(stream.readByte());
|
||||||
int position = stream.readInt();
|
int position = stream.readInt();
|
||||||
Object config = ver == 0 ? mapConfig(block, stream.readInt(), position) : TypeIO.readObject(Reads.get(stream), false, mapper);
|
Object config = ver == 0 ? mapConfig(block, stream.readInt(), position) : TypeIO.readObject(read, false, mapper);
|
||||||
byte rotation = stream.readByte();
|
byte rotation = stream.readByte();
|
||||||
if(block != Blocks.air){
|
if(block != Blocks.air){
|
||||||
tiles.add(new Stile(block, Point2.x(position), Point2.y(position), config, rotation));
|
tiles.add(new Stile(block, Point2.x(position), Point2.y(position), config, rotation));
|
||||||
@@ -619,6 +621,7 @@ public class Schematics implements Loadable{
|
|||||||
output.write(version);
|
output.write(version);
|
||||||
|
|
||||||
try(DataOutputStream stream = new DataOutputStream(new DeflaterOutputStream(output))){
|
try(DataOutputStream stream = new DataOutputStream(new DeflaterOutputStream(output))){
|
||||||
|
Writes write = new Writes(stream);
|
||||||
|
|
||||||
stream.writeShort(schematic.width);
|
stream.writeShort(schematic.width);
|
||||||
stream.writeShort(schematic.height);
|
stream.writeShort(schematic.height);
|
||||||
@@ -655,7 +658,7 @@ public class Schematics implements Loadable{
|
|||||||
for(Stile tile : schematic.tiles){
|
for(Stile tile : schematic.tiles){
|
||||||
stream.writeByte(blocks.orderedItems().indexOf(tile.block));
|
stream.writeByte(blocks.orderedItems().indexOf(tile.block));
|
||||||
stream.writeInt(Point2.pack(tile.x, tile.y));
|
stream.writeInt(Point2.pack(tile.x, tile.y));
|
||||||
TypeIO.writeObject(Writes.get(stream), tile.config);
|
TypeIO.writeObject(write, tile.config);
|
||||||
stream.writeByte(tile.rotation);
|
stream.writeByte(tile.rotation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class MapIO{
|
|||||||
SaveVersion ver = SaveIO.getSaveWriter(version);
|
SaveVersion ver = SaveIO.getSaveWriter(version);
|
||||||
if(ver == null) throw new IOException("Unknown save version: " + version + ". Are you trying to load a save from a newer version?");
|
if(ver == null) throw new IOException("Unknown save version: " + version + ". Are you trying to load a save from a newer version?");
|
||||||
StringMap tags = new StringMap();
|
StringMap tags = new StringMap();
|
||||||
ver.region("meta", stream, counter, in -> tags.putAll(ver.readStringMap(in)));
|
ver.readRegion("meta", stream, counter, in -> tags.putAll(ver.readStringMap(in)));
|
||||||
return new Map(file, tags.getInt("width"), tags.getInt("height"), tags, custom, version, Version.build);
|
return new Map(file, tags.getInt("width"), tags.getInt("height"), tags, custom, version, Version.build);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ public class MapIO{
|
|||||||
int version = stream.readInt();
|
int version = stream.readInt();
|
||||||
SaveVersion ver = SaveIO.getSaveWriter(version);
|
SaveVersion ver = SaveIO.getSaveWriter(version);
|
||||||
if(ver == null) throw new IOException("Unknown save version: " + version + ". Are you trying to load a save from a newer version?");
|
if(ver == null) throw new IOException("Unknown save version: " + version + ". Are you trying to load a save from a newer version?");
|
||||||
ver.region("meta", stream, counter, ver::readStringMap);
|
ver.readRegion("meta", stream, counter, ver::readStringMap);
|
||||||
|
|
||||||
Pixmap floors = new Pixmap(map.width, map.height);
|
Pixmap floors = new Pixmap(map.width, map.height);
|
||||||
Pixmap walls = new Pixmap(map.width, map.height);
|
Pixmap walls = new Pixmap(map.width, map.height);
|
||||||
@@ -96,8 +96,8 @@ public class MapIO{
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ver.region("content", stream, counter, ver::readContentHeader);
|
ver.readRegion("content", stream, counter, ver::readContentHeader);
|
||||||
ver.region("preview_map", stream, counter, in -> ver.readMap(in, new WorldContext(){
|
ver.readRegion("preview_map", stream, counter, in -> ver.readMap(in, new WorldContext(){
|
||||||
@Override public void resize(int width, int height){}
|
@Override public void resize(int width, int height){}
|
||||||
@Override public boolean isGenerating(){return false;}
|
@Override public boolean isGenerating(){return false;}
|
||||||
@Override public void begin(){
|
@Override public void begin(){
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package mindustry.io;
|
|||||||
|
|
||||||
import arc.struct.*;
|
import arc.struct.*;
|
||||||
import arc.struct.ObjectMap.*;
|
import arc.struct.ObjectMap.*;
|
||||||
import arc.util.*;
|
|
||||||
import arc.util.io.*;
|
import arc.util.io.*;
|
||||||
import mindustry.world.*;
|
import mindustry.world.*;
|
||||||
|
|
||||||
@@ -75,25 +74,22 @@ public abstract class SaveFileReader{
|
|||||||
"slag", "molten-slag"
|
"slag", "molten-slag"
|
||||||
);
|
);
|
||||||
|
|
||||||
protected final ReusableByteOutStream byteOutput = new ReusableByteOutStream(), byteOutput2 = new ReusableByteOutStream();
|
protected static final ReusableByteOutStream byteOutput = new ReusableByteOutStream(), byteOutput2 = new ReusableByteOutStream();
|
||||||
protected final DataOutputStream dataBytes = new DataOutputStream(byteOutput), dataBytes2 = new DataOutputStream(byteOutput2);
|
protected static final DataOutputStream dataBytes = new DataOutputStream(byteOutput), dataBytes2 = new DataOutputStream(byteOutput2);
|
||||||
protected final ReusableByteOutStream byteOutputSmall = new ReusableByteOutStream();
|
protected static final Writes writes1 = new Writes(dataBytes), writes2 = new Writes(dataBytes2);
|
||||||
protected final DataOutputStream dataBytesSmall = new DataOutputStream(byteOutputSmall);
|
protected static final Reads chunkReads = new Reads(null);
|
||||||
protected boolean chunkNested = false;
|
protected static boolean chunkNested = false;
|
||||||
|
|
||||||
protected int lastRegionLength;
|
|
||||||
protected @Nullable CounterInputStream currCounter;
|
|
||||||
|
|
||||||
public static String mapFallback(String name){
|
public static String mapFallback(String name){
|
||||||
return fallback.get(name, name);
|
return fallback.get(name, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void region(String name, DataInput stream, CounterInputStream counter, IORunner<DataInput> cons) throws IOException{
|
//TODO: unify readRegion with readChunk, they do the same thing and both should have good error messages
|
||||||
|
public void readRegion(String name, DataInput stream, CounterInputStream counter, IORunner<DataInput> cons) throws IOException{
|
||||||
counter.resetCount();
|
counter.resetCount();
|
||||||
this.currCounter = counter;
|
|
||||||
int length;
|
int length;
|
||||||
try{
|
try{
|
||||||
length = readChunk(stream, cons);
|
length = readChunk(stream, (chunkStream, len) -> cons.accept(chunkStream));
|
||||||
}catch(Throwable e){
|
}catch(Throwable e){
|
||||||
throw new IOException("Error reading region \"" + name + "\".", e);
|
throw new IOException("Error reading region \"" + name + "\".", e);
|
||||||
}
|
}
|
||||||
@@ -103,75 +99,74 @@ public abstract class SaveFileReader{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void region(String name, DataOutput stream, IORunner<DataOutput> cons) throws IOException{
|
public void writeRegion(String name, DataOutput stream, IORunner<DataOutput> cons) throws IOException{
|
||||||
try{
|
try{
|
||||||
writeChunk(stream, cons);
|
writeChunk(stream, writes -> cons.accept(writes.output));
|
||||||
}catch(Throwable e){
|
}catch(Throwable e){
|
||||||
throw new IOException("Error writing region \"" + name + "\".", e);
|
throw new IOException("Error writing region \"" + name + "\".", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeChunk(DataOutput output, IORunner<DataOutput> runner) throws IOException{
|
|
||||||
writeChunk(output, false, runner);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Write a chunk of input to the stream. An integer of some length is written first, followed by the data. */
|
/** 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 isShort, IORunner<DataOutput> runner) throws IOException{
|
public void writeChunk(DataOutput output, IORunner<Writes> runner) throws IOException{
|
||||||
|
|
||||||
//TODO awful
|
|
||||||
boolean wasNested = chunkNested;
|
boolean wasNested = chunkNested;
|
||||||
if(!isShort){
|
|
||||||
chunkNested = true;
|
chunkNested = true;
|
||||||
}
|
|
||||||
ReusableByteOutStream dout =
|
//regions can be nested once, so use a different output if it's already nested
|
||||||
isShort ? byteOutputSmall :
|
ReusableByteOutStream dout = wasNested ? byteOutput2 : byteOutput;
|
||||||
wasNested ? byteOutput2 :
|
|
||||||
byteOutput;
|
|
||||||
try{
|
try{
|
||||||
//reset output position
|
//reset output position
|
||||||
dout.reset();
|
dout.reset();
|
||||||
//write the needed info
|
//write the needed info
|
||||||
runner.accept(
|
runner.accept(wasNested ? writes2 : writes1);
|
||||||
isShort ? dataBytesSmall :
|
|
||||||
wasNested ? dataBytes2 :
|
|
||||||
dataBytes
|
|
||||||
);
|
|
||||||
|
|
||||||
|
//write the length of the stream contents as an int, then write the contents
|
||||||
int length = dout.size();
|
int length = dout.size();
|
||||||
//write length (either int or byte) followed by the output bytes
|
output.writeInt(length);
|
||||||
if(!isShort){
|
|
||||||
output.writeInt(length);
|
|
||||||
}else{
|
|
||||||
if(length > 65535){
|
|
||||||
throw new IOException("Byte write length exceeded: " + length + " > 65535");
|
|
||||||
}
|
|
||||||
output.writeShort(length);
|
|
||||||
}
|
|
||||||
output.write(dout.getBytes(), 0, length);
|
output.write(dout.getBytes(), 0, length);
|
||||||
}finally{
|
}finally{
|
||||||
chunkNested = wasNested;
|
chunkNested = wasNested;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int readChunk(DataInput input, IORunner<DataInput> runner) throws IOException{
|
|
||||||
return readChunk(input, false, runner);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Reads a chunk of some length. Use the runner for reading to catch more descriptive errors. */
|
/** Reads a chunk of some length. Use the runner for reading to catch more descriptive errors. */
|
||||||
public int readChunk(DataInput input, boolean isShort, IORunner<DataInput> runner) throws IOException{
|
public int readChunk(DataInput input, IORunnerLength<DataInput> runner) throws IOException{
|
||||||
int length = isShort ? input.readUnsignedShort() : input.readInt();
|
//TODO: it would be really nice to support counting here to detect serialization errors
|
||||||
lastRegionLength = length;
|
int length = input.readInt();
|
||||||
runner.accept(input);
|
runner.accept(input, length);
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void skipChunk(DataInput input) throws IOException{
|
/** Reads a chunk of some length. Use the runner for reading to catch more descriptive errors. */
|
||||||
skipChunk(input, false);
|
public int readChunkReads(DataInput input, IORunnerLength<Reads> runner) throws IOException{
|
||||||
|
return readChunk(input, (in, length) -> {
|
||||||
|
chunkReads.input = in;
|
||||||
|
runner.accept(chunkReads, length);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Skip a chunk completely, discarding the bytes. */
|
/** Skip a chunk completely, discarding the bytes. */
|
||||||
public void skipChunk(DataInput input, boolean isShort) throws IOException{
|
public void skipChunk(DataInput input) throws IOException{
|
||||||
int length = readChunk(input, isShort, t -> {});
|
int length = readChunk(input, (t, len) -> {});
|
||||||
|
int skipped = input.skipBytes(length);
|
||||||
|
if(length != skipped){
|
||||||
|
throw new IOException("Could not skip bytes. Expected length: " + length + "; Actual length: " + skipped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reads a legacy chunk where the length is only 2 bytes. */
|
||||||
|
public int readLegacyShortChunk(DataInput input, IORunnerLength<Reads> runner) throws IOException{
|
||||||
|
int length = input.readUnsignedShort();
|
||||||
|
chunkReads.input = input;
|
||||||
|
runner.accept(chunkReads, length);
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Skip a legacy chunk completely, discarding the bytes. */
|
||||||
|
public void skipLegacyShortChunk(DataInput input) throws IOException{
|
||||||
|
int length = readLegacyShortChunk(input, (t, len) -> {});
|
||||||
int skipped = input.skipBytes(length);
|
int skipped = input.skipBytes(length);
|
||||||
if(length != skipped){
|
if(length != skipped){
|
||||||
throw new IOException("Could not skip bytes. Expected length: " + length + "; Actual length: " + skipped);
|
throw new IOException("Could not skip bytes. Expected length: " + length + "; Actual length: " + skipped);
|
||||||
@@ -203,10 +198,18 @@ public abstract class SaveFileReader{
|
|||||||
void accept(T stream) throws IOException;
|
void accept(T stream) throws IOException;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface IORunnerLength<T>{
|
||||||
|
void accept(T stream, int length) throws IOException;
|
||||||
|
}
|
||||||
|
|
||||||
public interface CustomChunk{
|
public interface CustomChunk{
|
||||||
void write(DataOutput stream) throws IOException;
|
void write(DataOutput stream) throws IOException;
|
||||||
void read(DataInput stream) throws IOException;
|
void read(DataInput stream) throws IOException;
|
||||||
|
|
||||||
|
default void read(DataInput stream, int length) throws IOException{
|
||||||
|
read(stream);
|
||||||
|
}
|
||||||
|
|
||||||
/** @return whether this chunk is enabled at all */
|
/** @return whether this chunk is enabled at all */
|
||||||
default boolean shouldWrite(){
|
default boolean shouldWrite(){
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class SaveIO{
|
|||||||
/** Save format header. */
|
/** Save format header. */
|
||||||
public static final byte[] header = {'M', 'S', 'A', 'V'};
|
public static final byte[] header = {'M', 'S', 'A', 'V'};
|
||||||
public static final IntMap<SaveVersion> versions = new IntMap<>();
|
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(), new Save9());
|
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(), new Save10());
|
||||||
|
|
||||||
static{
|
static{
|
||||||
for(SaveVersion version : versionArray){
|
for(SaveVersion version : versionArray){
|
||||||
|
|||||||
@@ -29,12 +29,6 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
|
|
||||||
public final int version;
|
public final int version;
|
||||||
|
|
||||||
//HACK stores the last read build of the save file, valid after read meta call
|
|
||||||
protected int lastReadBuild;
|
|
||||||
//stores entity mappings for use after readEntityMapping
|
|
||||||
//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.
|
* 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.
|
* @param name a mod-specific, unique name for identifying this chunk. Prefixing is recommended.
|
||||||
@@ -69,26 +63,26 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public 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, in -> readMeta(in, context));
|
readRegion("meta", stream, counter, in -> readMeta(in, context));
|
||||||
region("content", stream, counter, this::readContentHeader);
|
readRegion("content", stream, counter, this::readContentHeader);
|
||||||
|
|
||||||
try{
|
try{
|
||||||
region("map", stream, counter, in -> readMap(in, context));
|
readRegion("map", stream, counter, in -> readMap(in, context));
|
||||||
region("entities", stream, counter, this::readEntities);
|
readRegion("entities", stream, counter, this::readEntities);
|
||||||
if(version >= 8) region("markers", stream, counter, this::readMarkers);
|
if(version >= 8) readRegion("markers", stream, counter, this::readMarkers);
|
||||||
region("custom", stream, counter, this::readCustomChunks);
|
readRegion("custom", stream, counter, this::readCustomChunks);
|
||||||
}finally{
|
}finally{
|
||||||
content.setTemporaryMapper(null);
|
content.setTemporaryMapper(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(DataOutputStream stream, StringMap extraTags) throws IOException{
|
public void write(DataOutputStream stream, StringMap extraTags) throws IOException{
|
||||||
region("meta", stream, out -> writeMeta(out, extraTags));
|
writeRegion("meta", stream, out -> writeMeta(out, extraTags));
|
||||||
region("content", stream, this::writeContentHeader);
|
writeRegion("content", stream, this::writeContentHeader);
|
||||||
region("map", stream, this::writeMap);
|
writeRegion("map", stream, this::writeMap);
|
||||||
region("entities", stream, this::writeEntities);
|
writeRegion("entities", stream, this::writeEntities);
|
||||||
region("markers", stream, this::writeMarkers);
|
writeRegion("markers", stream, this::writeMarkers);
|
||||||
region("custom", stream, s -> writeCustomChunks(s, false));
|
writeRegion("custom", stream, s -> writeCustomChunks(s, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeCustomChunks(DataOutput stream, boolean net) throws IOException{
|
public void writeCustomChunks(DataOutput stream, boolean net) throws IOException{
|
||||||
@@ -98,7 +92,7 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
var chunk = customChunks.get(chunkName);
|
var chunk = customChunks.get(chunkName);
|
||||||
stream.writeUTF(chunkName);
|
stream.writeUTF(chunkName);
|
||||||
|
|
||||||
writeChunk(stream, false, chunk::write);
|
writeChunk(stream, writes -> chunk.write(writes.output));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +102,7 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
String name = stream.readUTF();
|
String name = stream.readUTF();
|
||||||
var chunk = customChunks.get(name);
|
var chunk = customChunks.get(name);
|
||||||
if(chunk != null){
|
if(chunk != null){
|
||||||
readChunk(stream, false, chunk::read);
|
readChunk(stream, chunk::read);
|
||||||
}else{
|
}else{
|
||||||
skipChunk(stream);
|
skipChunk(stream);
|
||||||
}
|
}
|
||||||
@@ -163,7 +157,6 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
state.rules = JsonIO.read(Rules.class, map.get("rules", "{}"));
|
state.rules = JsonIO.read(Rules.class, map.get("rules", "{}"));
|
||||||
state.mapLocales = JsonIO.read(MapLocales.class, map.get("locales", "{}"));
|
state.mapLocales = JsonIO.read(MapLocales.class, map.get("locales", "{}"));
|
||||||
if(state.rules.spawns.isEmpty()) state.rules.spawns = waves.get();
|
if(state.rules.spawns.isEmpty()) state.rules.spawns = waves.get();
|
||||||
lastReadBuild = map.getInt("build", -1);
|
|
||||||
|
|
||||||
if(context.getSector() != null){
|
if(context.getSector() != null){
|
||||||
state.rules.sector = context.getSector();
|
state.rules.sector = context.getSector();
|
||||||
@@ -254,9 +247,9 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
if(tile.build != null){
|
if(tile.build != null){
|
||||||
if(tile.isCenter()){
|
if(tile.isCenter()){
|
||||||
stream.writeBoolean(true);
|
stream.writeBoolean(true);
|
||||||
writeChunk(stream, true, out -> {
|
writeChunk(stream, out -> {
|
||||||
out.writeByte(tile.build.version());
|
out.b(tile.build.version());
|
||||||
tile.build.writeAll(Writes.get(out));
|
tile.build.writeAll(out);
|
||||||
});
|
});
|
||||||
}else{
|
}else{
|
||||||
stream.writeBoolean(false);
|
stream.writeBoolean(false);
|
||||||
@@ -354,16 +347,16 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
if(isCenter){ //only read entity for center blocks
|
if(isCenter){ //only read entity for center blocks
|
||||||
if(block.hasBuilding()){
|
if(block.hasBuilding()){
|
||||||
try{
|
try{
|
||||||
readChunk(stream, true, in -> {
|
readChunkReads(stream, (in, len) -> {
|
||||||
byte revision = in.readByte();
|
byte revision = in.b();
|
||||||
tile.build.readAll(Reads.get(in), revision);
|
tile.build.readAll(in, revision);
|
||||||
});
|
});
|
||||||
}catch(Throwable e){
|
}catch(Throwable e){
|
||||||
throw new IOException("Failed to read tile entity of block: " + block, e);
|
throw new IOException("Failed to read tile entity of block: " + block, e);
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
//skip the entity region, as the entity and its IO code are now gone
|
//skip the entity region, as the entity and its IO code are now gone
|
||||||
skipChunk(stream, true);
|
skipChunk(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.onReadBuilding();
|
context.onReadBuilding();
|
||||||
@@ -393,6 +386,9 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
//write team data with entities.
|
//write team data with entities.
|
||||||
Seq<TeamData> data = state.teams.getActive().copy();
|
Seq<TeamData> data = state.teams.getActive().copy();
|
||||||
if(!data.contains(Team.sharded.data())) data.add(Team.sharded.data());
|
if(!data.contains(Team.sharded.data())) data.add(Team.sharded.data());
|
||||||
|
|
||||||
|
Writes writes = new Writes(stream);
|
||||||
|
|
||||||
stream.writeInt(data.size);
|
stream.writeInt(data.size);
|
||||||
for(TeamData team : data){
|
for(TeamData team : data){
|
||||||
stream.writeInt(team.team.id);
|
stream.writeInt(team.team.id);
|
||||||
@@ -402,7 +398,7 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
stream.writeShort(block.y);
|
stream.writeShort(block.y);
|
||||||
stream.writeShort(block.rotation);
|
stream.writeShort(block.rotation);
|
||||||
stream.writeShort(block.block.id);
|
stream.writeShort(block.block.id);
|
||||||
TypeIO.writeObject(Writes.get(stream), block.config);
|
TypeIO.writeObject(writes, block.config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -412,11 +408,11 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
for(Entityc entity : Groups.all){
|
for(Entityc entity : Groups.all){
|
||||||
if(!entity.serialize()) continue;
|
if(!entity.serialize()) continue;
|
||||||
|
|
||||||
writeChunk(stream, true, out -> {
|
writeChunk(stream, out -> {
|
||||||
out.writeByte(entity.classId());
|
out.b(entity.classId());
|
||||||
out.writeInt(entity.id());
|
out.i(entity.id());
|
||||||
entity.beforeWrite();
|
entity.beforeWrite();
|
||||||
entity.write(Writes.get(out));
|
entity.write(out);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -446,13 +442,14 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
public void readTeamBlocks(DataInput stream) throws IOException{
|
public void readTeamBlocks(DataInput stream) throws IOException{
|
||||||
int teamc = stream.readInt();
|
int teamc = stream.readInt();
|
||||||
|
|
||||||
|
var reads = new Reads(stream);
|
||||||
|
|
||||||
for(int i = 0; i < teamc; i++){
|
for(int i = 0; i < teamc; i++){
|
||||||
Team team = Team.get(stream.readInt());
|
Team team = Team.get(stream.readInt());
|
||||||
TeamData data = team.data();
|
TeamData data = team.data();
|
||||||
int blocks = stream.readInt();
|
int blocks = stream.readInt();
|
||||||
data.plans.clear();
|
data.plans.clear();
|
||||||
data.plans.ensureCapacity(Math.min(blocks, 1000));
|
data.plans.ensureCapacity(Math.min(blocks, 1000));
|
||||||
var reads = Reads.get(stream);
|
|
||||||
var set = new IntSet();
|
var set = new IntSet();
|
||||||
|
|
||||||
for(int j = 0; j < blocks; j++){
|
for(int j = 0; j < blocks; j++){
|
||||||
@@ -466,25 +463,23 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readWorldEntities(DataInput stream) throws IOException{
|
public void readWorldEntities(DataInput stream, Prov[] mapping) throws IOException{
|
||||||
//entityMapping is null in older save versions, so use the default
|
|
||||||
var mapping = this.entityMapping == null ? EntityMapping.idMap : this.entityMapping;
|
|
||||||
|
|
||||||
int amount = stream.readInt();
|
int amount = stream.readInt();
|
||||||
for(int j = 0; j < amount; j++){
|
for(int j = 0; j < amount; j++){
|
||||||
readChunk(stream, true, in -> {
|
readChunkReads(stream, (in, len) -> {
|
||||||
int typeid = in.readUnsignedByte();
|
int typeid = in.ub();
|
||||||
if(mapping[typeid] == null){
|
if(mapping[typeid] == null){
|
||||||
in.skipBytes(lastRegionLength - 1);
|
in.skip(len - 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int id = in.readInt();
|
int id = in.i();
|
||||||
|
|
||||||
Entityc entity = (Entityc)mapping[typeid].get();
|
Entityc entity = (Entityc)mapping[typeid].get();
|
||||||
EntityGroup.checkNextId(id);
|
EntityGroup.checkNextId(id);
|
||||||
entity.id(id);
|
entity.id(id);
|
||||||
entity.read(Reads.get(in));
|
entity.read(in);
|
||||||
entity.add();
|
entity.add();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -492,9 +487,9 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
Groups.all.each(Entityc::afterReadAll);
|
Groups.all.each(Entityc::afterReadAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readEntityMapping(DataInput stream) throws IOException{
|
public Prov[] readEntityMapping(DataInput stream) throws IOException{
|
||||||
//copy entityMapping for further mutation; will be used in readWorldEntities
|
//copy entityMapping for further mutation; will be used in readWorldEntities
|
||||||
entityMapping = Arrays.copyOf(EntityMapping.idMap, EntityMapping.idMap.length);
|
Prov[] entityMapping = Arrays.copyOf(EntityMapping.idMap, EntityMapping.idMap.length);
|
||||||
|
|
||||||
short amount = stream.readShort();
|
short amount = stream.readShort();
|
||||||
for(int i = 0; i < amount; i++){
|
for(int i = 0; i < amount; i++){
|
||||||
@@ -504,12 +499,14 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
String name = stream.readUTF();
|
String name = stream.readUTF();
|
||||||
entityMapping[id] = EntityMapping.map(name);
|
entityMapping[id] = EntityMapping.map(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return entityMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readEntities(DataInput stream) throws IOException{
|
public void readEntities(DataInput stream) throws IOException{
|
||||||
readEntityMapping(stream);
|
var mapping = readEntityMapping(stream);
|
||||||
readTeamBlocks(stream);
|
readTeamBlocks(stream);
|
||||||
readWorldEntities(stream);
|
readWorldEntities(stream, mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readContentHeader(DataInput stream) throws IOException{
|
public void readContentHeader(DataInput stream) throws IOException{
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package mindustry.io.versions;
|
package mindustry.io.versions;
|
||||||
|
|
||||||
import arc.util.io.*;
|
import arc.util.io.*;
|
||||||
import mindustry.io.*;
|
|
||||||
import mindustry.world.*;
|
import mindustry.world.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@@ -9,7 +8,7 @@ import java.io.*;
|
|||||||
import static mindustry.Vars.*;
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
/** This version does not read custom chunk data (<= 6). */
|
/** This version does not read custom chunk data (<= 6). */
|
||||||
public class LegacyRegionSaveVersion extends SaveVersion{
|
public class LegacyRegionSaveVersion extends ShortChunkSaveVersion{
|
||||||
|
|
||||||
public LegacyRegionSaveVersion(int version){
|
public LegacyRegionSaveVersion(int version){
|
||||||
super(version);
|
super(version);
|
||||||
@@ -17,12 +16,12 @@ public class LegacyRegionSaveVersion extends SaveVersion{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public 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, in -> readMeta(in, context));
|
readRegion("meta", stream, counter, in -> readMeta(in, context));
|
||||||
region("content", stream, counter, this::readContentHeader);
|
readRegion("content", stream, counter, this::readContentHeader);
|
||||||
|
|
||||||
try{
|
try{
|
||||||
region("map", stream, counter, in -> readMap(in, context));
|
readRegion("map", stream, counter, in -> readMap(in, context));
|
||||||
region("entities", stream, counter, this::readEntities);
|
readRegion("entities", stream, counter, this::readEntities);
|
||||||
}finally{
|
}finally{
|
||||||
content.setTemporaryMapper(null);
|
content.setTemporaryMapper(null);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package mindustry.io.versions;
|
package mindustry.io.versions;
|
||||||
|
|
||||||
import arc.util.*;
|
import arc.util.*;
|
||||||
import arc.util.io.*;
|
|
||||||
import mindustry.content.*;
|
import mindustry.content.*;
|
||||||
import mindustry.game.*;
|
import mindustry.game.*;
|
||||||
import mindustry.world.*;
|
import mindustry.world.*;
|
||||||
@@ -61,8 +60,8 @@ public abstract class LegacySaveVersion extends LegacyRegionSaveVersion{
|
|||||||
|
|
||||||
if(block.hasBuilding()){
|
if(block.hasBuilding()){
|
||||||
try{
|
try{
|
||||||
readChunk(stream, true, in -> {
|
readLegacyShortChunk(stream, (in, len) -> {
|
||||||
byte version = in.readByte();
|
byte version = in.b();
|
||||||
//legacy impl of Building#read()
|
//legacy impl of Building#read()
|
||||||
tile.build.health = stream.readUnsignedShort();
|
tile.build.health = stream.readUnsignedShort();
|
||||||
byte packedrot = stream.readByte();
|
byte packedrot = stream.readByte();
|
||||||
@@ -72,14 +71,14 @@ public abstract class LegacySaveVersion extends LegacyRegionSaveVersion{
|
|||||||
tile.setTeam(Team.get(team));
|
tile.setTeam(Team.get(team));
|
||||||
tile.build.rotation = rotation;
|
tile.build.rotation = rotation;
|
||||||
|
|
||||||
if(tile.build.items != null) tile.build.items.read(Reads.get(stream), true);
|
if(tile.build.items != null) tile.build.items.read(in, true);
|
||||||
if(tile.build.power != null) tile.build.power.read(Reads.get(stream), true);
|
if(tile.build.power != null) tile.build.power.read(in, true);
|
||||||
if(tile.build.liquids != null) tile.build.liquids.read(Reads.get(stream), true);
|
if(tile.build.liquids != null) tile.build.liquids.read(in, true);
|
||||||
//skip cons.valid boolean, it's not very important here
|
//skip cons.valid boolean, it's not very important here
|
||||||
stream.readByte();
|
stream.readByte();
|
||||||
|
|
||||||
//read only from subclasses!
|
//read only from subclasses!
|
||||||
tile.build.read(Reads.get(in), version);
|
tile.build.read(in, version);
|
||||||
});
|
});
|
||||||
}catch(Throwable e){
|
}catch(Throwable e){
|
||||||
throw new IOException("Failed to read tile entity of block: " + block, e);
|
throw new IOException("Failed to read tile entity of block: " + block, e);
|
||||||
@@ -111,7 +110,7 @@ public abstract class LegacySaveVersion extends LegacyRegionSaveVersion{
|
|||||||
int amount = stream.readInt();
|
int amount = stream.readInt();
|
||||||
for(int j = 0; j < amount; j++){
|
for(int j = 0; j < amount; j++){
|
||||||
//simply skip all the entities
|
//simply skip all the entities
|
||||||
skipChunk(stream, true);
|
skipLegacyShortChunk(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package mindustry.io.versions;
|
package mindustry.io.versions;
|
||||||
|
|
||||||
import arc.func.*;
|
import arc.func.*;
|
||||||
import arc.util.io.*;
|
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@@ -14,21 +13,19 @@ public class LegacySaveVersion2 extends LegacyRegionSaveVersion{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readWorldEntities(DataInput stream) throws IOException{
|
public void readWorldEntities(DataInput stream, Prov[] mapping) throws IOException{
|
||||||
//entityMapping is null in older save versions, so use the default
|
|
||||||
Prov[] mapping = this.entityMapping == null ? EntityMapping.idMap : this.entityMapping;
|
|
||||||
|
|
||||||
int amount = stream.readInt();
|
int amount = stream.readInt();
|
||||||
for(int j = 0; j < amount; j++){
|
for(int j = 0; j < amount; j++){
|
||||||
readChunk(stream, true, in -> {
|
readLegacyShortChunk(stream, (in, len) -> {
|
||||||
int typeid = in.readUnsignedByte();
|
int typeid = in.ub();
|
||||||
if(mapping[typeid] == null){
|
if(mapping[typeid] == null){
|
||||||
in.skipBytes(lastRegionLength - 1);
|
in.skip(len - 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Entityc entity = (Entityc)mapping[typeid].get();
|
Entityc entity = (Entityc)mapping[typeid].get();
|
||||||
entity.read(Reads.get(in));
|
entity.read(in);
|
||||||
entity.add();
|
entity.add();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
11
core/src/mindustry/io/versions/Save10.java
Normal file
11
core/src/mindustry/io/versions/Save10.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package mindustry.io.versions;
|
||||||
|
|
||||||
|
import mindustry.io.*;
|
||||||
|
|
||||||
|
/** Removes short entity chunks, switching to 4 byte lengths for all chunks. */
|
||||||
|
public class Save10 extends SaveVersion{
|
||||||
|
|
||||||
|
public Save10(){
|
||||||
|
super(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package mindustry.io.versions;
|
package mindustry.io.versions;
|
||||||
|
|
||||||
|
import mindustry.gen.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
/** This version only reads entities, no entity ID mappings. */
|
/** This version only reads entities, no entity ID mappings. */
|
||||||
@@ -12,7 +14,7 @@ public class Save4 extends LegacySaveVersion2{
|
|||||||
@Override
|
@Override
|
||||||
public void readEntities(DataInput stream) throws IOException{
|
public void readEntities(DataInput stream) throws IOException{
|
||||||
readTeamBlocks(stream);
|
readTeamBlocks(stream);
|
||||||
readWorldEntities(stream);
|
readWorldEntities(stream, EntityMapping.idMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package mindustry.io.versions;
|
package mindustry.io.versions;
|
||||||
|
|
||||||
import mindustry.io.*;
|
public class Save7 extends ShortChunkSaveVersion{
|
||||||
|
|
||||||
public class Save7 extends SaveVersion{
|
|
||||||
|
|
||||||
public Save7(){
|
public Save7(){
|
||||||
super(7);
|
super(7);
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package mindustry.io.versions;
|
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. */
|
/** 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 class Save8 extends ShortChunkSaveVersion{
|
||||||
|
|
||||||
public Save8(){
|
public Save8(){
|
||||||
super(8);
|
super(8);
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
package mindustry.io.versions;
|
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. This version is the last one that uses short chunks. */
|
||||||
|
public class Save9 extends ShortChunkSaveVersion{
|
||||||
/** 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(){
|
public Save9(){
|
||||||
super(9);
|
super(9);
|
||||||
}
|
}
|
||||||
|
|||||||
153
core/src/mindustry/io/versions/ShortChunkSaveVersion.java
Normal file
153
core/src/mindustry/io/versions/ShortChunkSaveVersion.java
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
package mindustry.io.versions;
|
||||||
|
|
||||||
|
import arc.func.*;
|
||||||
|
import mindustry.content.*;
|
||||||
|
import mindustry.entities.*;
|
||||||
|
import mindustry.gen.*;
|
||||||
|
import mindustry.io.*;
|
||||||
|
import mindustry.world.*;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
|
public class ShortChunkSaveVersion extends SaveVersion{
|
||||||
|
|
||||||
|
public ShortChunkSaveVersion(int version){
|
||||||
|
super(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readWorldEntities(DataInput stream, Prov[] mapping) throws IOException{
|
||||||
|
|
||||||
|
int amount = stream.readInt();
|
||||||
|
for(int j = 0; j < amount; j++){
|
||||||
|
readLegacyShortChunk(stream, (in, len) -> {
|
||||||
|
int typeid = in.ub();
|
||||||
|
if(mapping[typeid] == null){
|
||||||
|
in.skip(len - 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int id = in.i();
|
||||||
|
|
||||||
|
Entityc entity = (Entityc)mapping[typeid].get();
|
||||||
|
EntityGroup.checkNextId(id);
|
||||||
|
entity.id(id);
|
||||||
|
entity.read(in);
|
||||||
|
entity.add();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Groups.all.each(Entityc::afterReadAll);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readMap(DataInput stream, WorldContext context) throws IOException{
|
||||||
|
int width = stream.readUnsignedShort();
|
||||||
|
int height = stream.readUnsignedShort();
|
||||||
|
|
||||||
|
boolean generating = context.isGenerating();
|
||||||
|
|
||||||
|
if(!generating) context.begin();
|
||||||
|
try{
|
||||||
|
|
||||||
|
context.resize(width, height);
|
||||||
|
|
||||||
|
//read floor and create tiles first
|
||||||
|
for(int i = 0; i < width * height; i++){
|
||||||
|
int x = i % width, y = i / width;
|
||||||
|
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);
|
||||||
|
|
||||||
|
for(int j = i + 1; j < i + 1 + consecutives; j++){
|
||||||
|
int newx = j % width, newy = j / width;
|
||||||
|
context.create(newx, newy, floorid, oreid, (short)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
i += consecutives;
|
||||||
|
}
|
||||||
|
|
||||||
|
//read blocks
|
||||||
|
for(int i = 0; i < width * height; i++){
|
||||||
|
Block block = content.block(stream.readShort());
|
||||||
|
Tile tile = context.tile(i);
|
||||||
|
if(block == null) block = Blocks.air;
|
||||||
|
boolean isCenter = true;
|
||||||
|
byte packedCheck = stream.readByte();
|
||||||
|
boolean hadEntity = (packedCheck & 1) != 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;
|
||||||
|
|
||||||
|
byte data = 0, floorData = 0, overlayData = 0;
|
||||||
|
int extraData = 0;
|
||||||
|
|
||||||
|
if(hadDataNew){
|
||||||
|
data = stream.readByte();
|
||||||
|
floorData = stream.readByte();
|
||||||
|
overlayData = stream.readByte();
|
||||||
|
extraData = stream.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hadEntity){
|
||||||
|
isCenter = stream.readBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
//set block only if this is the center; otherwise, it's handled elsewhere
|
||||||
|
if(isCenter){
|
||||||
|
tile.setBlock(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
//must be assigned after setBlock, because that can reset data
|
||||||
|
if(hadDataNew){
|
||||||
|
tile.data = data;
|
||||||
|
tile.floorData = floorData;
|
||||||
|
tile.overlayData = overlayData;
|
||||||
|
tile.extraData = extraData;
|
||||||
|
context.onReadTileData();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hadEntity){
|
||||||
|
if(isCenter){ //only read entity for center blocks
|
||||||
|
if(block.hasBuilding()){
|
||||||
|
try{
|
||||||
|
readLegacyShortChunk(stream, (in, len) -> {
|
||||||
|
byte revision = in.b();
|
||||||
|
tile.build.readAll(in, revision);
|
||||||
|
});
|
||||||
|
}catch(Throwable e){
|
||||||
|
throw new IOException("Failed to read tile entity of block: " + block, e);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
//skip the entity region, as the entity and its IO code are now gone
|
||||||
|
skipLegacyShortChunk(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.onReadBuilding();
|
||||||
|
}
|
||||||
|
}else if(hadDataOld || hadDataNew){ //never read consecutive blocks if there's any kind of data
|
||||||
|
if(hadDataOld){
|
||||||
|
tile.setBlock(block);
|
||||||
|
//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();
|
||||||
|
|
||||||
|
for(int j = i + 1; j < i + 1 + consecutives; j++){
|
||||||
|
context.tile(j).setBlock(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
i += consecutives;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}finally{
|
||||||
|
if(!generating) context.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -377,7 +377,7 @@ public class LExecutor{
|
|||||||
if(unit instanceof Payloadc pay){
|
if(unit instanceof Payloadc pay){
|
||||||
//units
|
//units
|
||||||
if(p1.bool()){
|
if(p1.bool()){
|
||||||
Unit result = Units.closest(unit.team, unit.x, unit.y, unit.type.hitSize * 2f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize * 1.2f));
|
Unit result = Units.closest(unit.team, unit.x, unit.y, unit.type.hitSize * 2f, u -> u != unit && u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize * 1.2f));
|
||||||
|
|
||||||
if(result != null){
|
if(result != null){
|
||||||
Call.pickedUnitPayload(unit, result);
|
Call.pickedUnitPayload(unit, result);
|
||||||
|
|||||||
@@ -80,8 +80,8 @@ public class SwitchBlock extends Block{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readAll(Reads read, byte revision){
|
public void read(Reads read, byte revision){
|
||||||
super.readAll(read, revision);
|
super.read(read, revision);
|
||||||
|
|
||||||
if(revision == 1){
|
if(revision == 1){
|
||||||
enabled = read.bool();
|
enabled = read.bool();
|
||||||
|
|||||||
@@ -26,4 +26,4 @@ org.gradle.caching=true
|
|||||||
org.gradle.internal.http.socketTimeout=100000
|
org.gradle.internal.http.socketTimeout=100000
|
||||||
org.gradle.internal.http.connectionTimeout=100000
|
org.gradle.internal.http.connectionTimeout=100000
|
||||||
android.enableR8.fullMode=false
|
android.enableR8.fullMode=false
|
||||||
archash=390d5e8665
|
archash=2c95712087
|
||||||
|
|||||||
Reference in New Issue
Block a user