Map conversion code

This commit is contained in:
Anuken
2019-05-07 17:30:37 -04:00
parent 1b77247c40
commit 53167a3b52
10 changed files with 106 additions and 32 deletions

View File

@@ -104,8 +104,10 @@ public class Vars{
public static FileHandle customMapDirectory;
/** data subdirectory used for saves */
public static FileHandle saveDirectory;
/** old map file extension, for conversion */
public static final String oldMapExtension = "mmap";
/** map file extension */
public static final String mapExtension = "mmap";
public static final String mapExtension = "msav";
/** save file extension */
public static final String saveExtension = "msav";

View File

@@ -477,7 +477,7 @@ public class NetServer implements ApplicationListener{
sent++;
if(syncStream.position() > maxSnapshotSize){
if(syncStream.size() > maxSnapshotSize){
dataStream.close();
byte[] syncBytes = syncStream.toByteArray();
Call.onEntitySnapshot(player.con.id, (byte)group.getID(), (short)sent, (short)syncBytes.length, Net.compressSnapshot(syncBytes));

View File

@@ -2,7 +2,6 @@ package io.anuke.mindustry.core;
import io.anuke.annotations.Annotations.Nullable;
import io.anuke.arc.*;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.IntArray;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.math.geom.Geometry;
@@ -18,7 +17,7 @@ import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.maps.*;
import io.anuke.mindustry.maps.generators.Generator;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.type.Zone;
import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.blocks.BlockPart;
@@ -33,11 +32,10 @@ public class World implements ApplicationListener{
private Map currentMap;
private Tile[][] tiles;
private Array<Tile> tempTiles = new Array<>();
private boolean generating, invalidMap;
public World(){
maps.load();
Core.app.post(maps::load);
}
@Override

View File

@@ -1,6 +1,6 @@
package io.anuke.mindustry.io;
import io.anuke.arc.collection.StringMap;
import io.anuke.arc.collection.*;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.Pixmap;
@@ -10,6 +10,7 @@ import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.io.MapIO.TileProvider;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.LegacyColorMapper.LegacyBlock;
import io.anuke.mindustry.world.blocks.BlockPart;
@@ -18,12 +19,25 @@ import io.anuke.mindustry.world.blocks.Floor;
import java.io.*;
import java.util.zip.InflaterInputStream;
import static io.anuke.mindustry.Vars.bufferSize;
import static io.anuke.mindustry.Vars.content;
import static io.anuke.mindustry.Vars.*;
/** Map IO for the "old" .mmap format.
* Differentiate between legacy maps and new maps by checking the extension (or the header).*/
public class LegacyMapIO{
private static final ObjectMap<String, String> fallback = ObjectMap.of("alpha-dart-mech-pad", "dart-mech-pad");
/* Convert a map from the old format to the new format. */
public static void convertMap(FileHandle in, FileHandle out) throws IOException{
Map map = readMap(in, true);
Tile[][] tiles = world.createTiles(map.width, map.height);
for(int x = 0; x < map.width; x++){
for(int y = 0; y < map.height; y++){
tiles[x][y] = new Tile(x, y);
}
}
readTiles(map, tiles);
MapIO.writeMap(out, map);
}
public static Map readMap(FileHandle file, boolean custom) throws IOException{
try(DataInputStream stream = new DataInputStream(file.read(1024))){
@@ -78,24 +92,40 @@ public class LegacyMapIO{
try(DataInputStream stream = new DataInputStream(new InflaterInputStream(input))){
try{
SaveIO.getSaveWriter().readContentHeader(stream);
byte mapped = stream.readByte();
IntMap<Block> idmap = new IntMap<>();
IntMap<String> namemap = new IntMap<>();
for(int i = 0; i < mapped; i++){
byte type = stream.readByte();
short total = stream.readShort();
for(int j = 0; j < total; j++){
String name = stream.readUTF();
if(type == 1){
Block res = content.getByName(ContentType.block, fallback.get(name, name));
idmap.put(j, res == null ? Blocks.air : res);
namemap.put(j, fallback.get(name, name));
}
}
}
//read floor and create tiles first
for(int i = 0; i < width * height; i++){
int x = i % width, y = i / width;
byte floorid = stream.readByte();
byte oreid = stream.readByte();
int floorid = stream.readUnsignedByte();
int oreid = stream.readUnsignedByte();
int consecutives = stream.readUnsignedByte();
Tile tile = tiles.get(x, y);
tile.setFloor((Floor)content.block(floorid));
tile.setOverlay(content.block(oreid));
tile.setFloor((Floor)idmap.get(floorid));
tile.setOverlay(idmap.get(oreid));
for(int j = i + 1; j < i + 1 + consecutives; j++){
int newx = j % width, newy = j / width;
Tile newTile = tiles.get(newx, newy);
newTile.setFloor((Floor)content.block(floorid));
newTile.setOverlay(content.block(oreid));
newTile.setFloor((Floor)idmap.get(floorid));
newTile.setOverlay(idmap.get(oreid));
}
i += consecutives;
@@ -104,7 +134,8 @@ public class LegacyMapIO{
//read blocks
for(int i = 0; i < width * height; i++){
int x = i % width, y = i / width;
Block block = content.block(stream.readByte());
int id = stream.readUnsignedByte();
Block block = idmap.get(id);
Tile tile = tiles.get(x, y);
//the spawn block is saved in the block tile layer in older maps, shift it to the overlay
@@ -114,7 +145,9 @@ public class LegacyMapIO{
tile.setOverlay(block);
}
if(tile.entity != null){
if(namemap.get(id).equals("part")){
stream.readByte(); //link
}else if(tile.entity != null){
byte tr = stream.readByte();
stream.readShort(); //read health (which is actually irrelevant)

View File

@@ -20,7 +20,7 @@ import java.util.zip.InflaterInputStream;
import static io.anuke.mindustry.Vars.bufferSize;
/** Reads and writes map files. */
//TODO does this class even need to exist??? move to Maps
//TODO does this class even need to exist??? move to Maps?
public class MapIO{
private static final int[] pngHeader = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};

View File

@@ -11,17 +11,18 @@ import java.io.*;
public abstract class SaveFileReader{
protected final ReusableByteOutStream byteOutput = new ReusableByteOutStream();
protected final DataOutputStream dataBytes = new DataOutputStream(byteOutput);
protected final ObjectMap<String, String> fallback = ObjectMap.of("alpha-dart-mech-pad", "dart-mech-pad");
protected final ObjectMap<String, String> fallback = ObjectMap.of();
protected void region(String name, DataInput stream, CounterInputStream counter, IORunner<DataInput> cons) throws IOException{
counter.resetCount();
int length;
try{
length = readChunk(stream, cons);
}catch(Throwable e){
throw new IOException("Error reading region \"" + name + "\".", e);
}
if(length != counter.count() + 4){
throw new IOException("Error reading region \"" + name + "\": read length mismatch. Expected: " + length + "; Actual: " + (counter.count() + 4));
if(length != counter.count() - 4){
throw new IOException("Error reading region \"" + name + "\": read length mismatch. Expected: " + length + "; Actual: " + (counter.count() - 4));
}
}
@@ -40,10 +41,10 @@ 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{
//reset output position
byteOutput.position(0);
//writer the needed info
byteOutput.reset();
//write the needed info
runner.accept(dataBytes);
int length = byteOutput.position();
int length = byteOutput.size();
//write length (either int or byte) followed by the output bytes
if(!isByte){
output.writeInt(length);

View File

@@ -58,11 +58,19 @@ public class SaveIO{
}
public static boolean isSaveValid(int slot){
return isSaveValid(getSlotStream(slot)) || isSaveValid(getBackupSlotStream(slot));
try{ //file may not even exist at all, so catch that too
return isSaveValid(getSlotStream(slot)) || isSaveValid(getBackupSlotStream(slot));
}catch(Exception e){
return false;
}
}
public static boolean isSaveValid(FileHandle file){
return isSaveValid(new DataInputStream(new InflaterInputStream(file.read(bufferSize))));
try{
return isSaveValid(new DataInputStream(new InflaterInputStream(file.read(bufferSize))));
}catch(Exception e){
return false;
}
}
public static boolean isSaveValid(DataInputStream stream){

View File

@@ -1,6 +1,7 @@
package io.anuke.mindustry.io;
import io.anuke.arc.collection.*;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.StringMap;
import io.anuke.arc.util.Time;
import io.anuke.arc.util.io.CounterInputStream;
import io.anuke.mindustry.entities.Entities;
@@ -52,7 +53,7 @@ public abstract class SaveVersion extends SaveFileReader{
"saved", Time.millis(),
"playtime", headless ? 0 : control.saves.getTotalPlaytime(),
"build", Version.build,
"mapname", world.getMap().name(),
"mapname", world.getMap() == null ? "unknown" : world.getMap().name(),
"wave", state.wave,
"wavetime", state.wavetime,
"stats", Serialization.writeStatsJson(state.stats),

View File

@@ -8,6 +8,7 @@ import io.anuke.arc.util.Disposable;
import io.anuke.arc.util.Log;
import io.anuke.arc.util.serialization.Json;
import io.anuke.mindustry.game.SpawnGroup;
import io.anuke.mindustry.io.LegacyMapIO;
import io.anuke.mindustry.io.MapIO;
import java.io.IOException;
@@ -59,6 +60,15 @@ public class Maps implements Disposable{
/** Load all maps. Should be called at application start. */
public void load(){
try{
//TODO remove, this is only for testing
for(FileHandle in : Core.files.absolute("/home/anuke/Projects/Mindustry/core/assets/maps").list()){
if(in.extension().equalsIgnoreCase(oldMapExtension)){
Log.info("Converting {0}...", in);
LegacyMapIO.convertMap(in, in.sibling(in.nameWithoutExtension() + "." + mapExtension));
Log.info("Converted {0}", in);
}
}
for(String name : defaultMapNames){
FileHandle file = Core.files.internal("maps/" + name + "." + mapExtension);
loadMap(file, false);
@@ -183,6 +193,26 @@ public class Maps implements Disposable{
}
private void loadCustomMaps(){
boolean convertedAny = false;
for(FileHandle file : customMapDirectory.list()){
if(file.extension().equalsIgnoreCase(oldMapExtension)){
convertedAny = true;
try{
LegacyMapIO.convertMap(file, file.sibling(file.nameWithoutExtension() + "." + mapExtension));
//TODO delete so conversion doesn't happen again
//file.delete();
}catch(IOException e){
//don't convert
Log.err(e);
}
}
}
//free up any potential memory that was used up during conversion
if(convertedAny){
world.createTiles(0, 0);
}
for(FileHandle file : customMapDirectory.list()){
try{
if(file.extension().equalsIgnoreCase(mapExtension)){

View File

@@ -60,9 +60,10 @@ public class MapGenerator extends Generator{
@Override
public void init(Loadout loadout){
this.loadout = loadout;
map = world.maps.loadInternalMap(mapName);
width = map.width;
height = map.height;
//TODO uncomment once conversion works
//map = world.maps.loadInternalMap(mapName);
//width = map.width;
//height = map.height;
}
@Override