many things
This commit is contained in:
@@ -117,7 +117,7 @@ public class NetClient implements ApplicationListener{
|
||||
}
|
||||
|
||||
//called on all clients
|
||||
@Remote(called = Loc.server, targets = Loc.server)
|
||||
@Remote(called = Loc.server, targets = Loc.server, variants = Variant.both)
|
||||
public static void sendMessage(String message, String sender, Player playersender){
|
||||
if(Vars.ui != null){
|
||||
Vars.ui.chatfrag.addMessage(message, sender);
|
||||
|
||||
@@ -202,6 +202,7 @@ public class World implements ApplicationListener{
|
||||
return state.rules.zone;
|
||||
}
|
||||
|
||||
//TODO move to Control
|
||||
public void playZone(Zone zone){
|
||||
ui.loadAnd(() -> {
|
||||
logic.reset();
|
||||
|
||||
@@ -72,6 +72,11 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte version(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float lifetime(){
|
||||
return lifetime;
|
||||
@@ -151,7 +156,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSave(DataInput stream) throws IOException{
|
||||
public void readSave(DataInput stream, byte version) throws IOException{
|
||||
this.loadedPosition = stream.readInt();
|
||||
this.lifetime = stream.readFloat();
|
||||
this.time = stream.readFloat();
|
||||
|
||||
@@ -143,6 +143,11 @@ public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrai
|
||||
return liquid.flammability * amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte version(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hitbox(Rectangle rectangle){
|
||||
rectangle.setCenter(x, y).setSize(tilesize);
|
||||
@@ -245,7 +250,7 @@ public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrai
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSave(DataInput stream) throws IOException{
|
||||
public void readSave(DataInput stream, byte version) throws IOException{
|
||||
this.loadedPosition = stream.readInt();
|
||||
this.x = stream.readFloat();
|
||||
this.y = stream.readFloat();
|
||||
|
||||
@@ -4,4 +4,5 @@ package io.anuke.mindustry.entities.traits;
|
||||
* Marks an entity as serializable.
|
||||
*/
|
||||
public interface SaveTrait extends Entity, TypeTrait, Saveable{
|
||||
byte version();
|
||||
}
|
||||
|
||||
@@ -4,6 +4,5 @@ import java.io.*;
|
||||
|
||||
public interface Saveable{
|
||||
void writeSave(DataOutput stream) throws IOException;
|
||||
|
||||
void readSave(DataInput stream) throws IOException;
|
||||
void readSave(DataInput stream, byte version) throws IOException;
|
||||
}
|
||||
|
||||
@@ -41,15 +41,6 @@ public interface SyncTrait extends Entity, TypeTrait{
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Whether this entity is clipped and not synced when out of viewport. */
|
||||
default boolean isClipped(){
|
||||
return true;
|
||||
}
|
||||
|
||||
default float clipSize(){
|
||||
return (this instanceof DrawTrait ? ((DrawTrait)this).drawSize() : 8f);
|
||||
}
|
||||
|
||||
//Read and write sync data, usually position
|
||||
void write(DataOutput data) throws IOException;
|
||||
|
||||
|
||||
@@ -23,9 +23,7 @@ public interface TypeTrait{
|
||||
lastRegisteredID[0]++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a syncable type by ID.
|
||||
*/
|
||||
/**Gets a syncable type by ID.*/
|
||||
static Supplier<? extends TypeTrait> getTypeByID(int id){
|
||||
if(id == -1){
|
||||
throw new IllegalArgumentException("Attempt to retrieve invalid entity type ID! Did you forget to set it in ContentLoader.registerTypes()?");
|
||||
|
||||
@@ -304,11 +304,6 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
return type.hitsize * 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float clipSize(){
|
||||
return isBoss() ? 10000000000f : super.clipSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeath(){
|
||||
Call.onUnitDeath(this);
|
||||
@@ -336,6 +331,11 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
return unitGroups[team.ordinal()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte version(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSave(DataOutput stream) throws IOException{
|
||||
super.writeSave(stream);
|
||||
@@ -344,8 +344,8 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSave(DataInput stream) throws IOException{
|
||||
super.readSave(stream);
|
||||
public void readSave(DataInput stream, byte version) throws IOException{
|
||||
super.readSave(stream, version);
|
||||
byte type = stream.readByte();
|
||||
this.spawner = stream.readInt();
|
||||
|
||||
@@ -363,7 +363,9 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
@Override
|
||||
public void read(DataInput data) throws IOException{
|
||||
float lastx = x, lasty = y, lastrot = rotation;
|
||||
super.readSave(data);
|
||||
|
||||
super.readSave(data, version());
|
||||
|
||||
this.type = content.getByID(ContentType.unit, data.readByte());
|
||||
this.spawner = data.readInt();
|
||||
|
||||
|
||||
@@ -817,8 +817,8 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
|
||||
//region read and write methods
|
||||
|
||||
@Override
|
||||
public boolean isClipped(){
|
||||
return false;
|
||||
public byte version(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -833,7 +833,7 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSave(DataInput stream) throws IOException{
|
||||
public void readSave(DataInput stream, byte version) throws IOException{
|
||||
boolean local = stream.readBoolean();
|
||||
|
||||
if(local){
|
||||
@@ -844,14 +844,14 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
|
||||
lastSpawner = (SpawnerTrait)stile.entity;
|
||||
}
|
||||
Player player = headless ? this : Vars.player;
|
||||
player.readSaveSuper(stream);
|
||||
player.readSaveSuper(stream, version);
|
||||
player.mech = content.getByID(ContentType.mech, mechid);
|
||||
player.dead = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void readSaveSuper(DataInput stream) throws IOException{
|
||||
super.readSave(stream);
|
||||
private void readSaveSuper(DataInput stream, byte version) throws IOException{
|
||||
super.readSave(stream, version);
|
||||
|
||||
add();
|
||||
}
|
||||
@@ -873,7 +873,9 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
|
||||
@Override
|
||||
public void read(DataInput buffer) throws IOException{
|
||||
float lastx = x, lasty = y, lastrot = rotation, lastvx = velocity.x, lastvy = velocity.y;
|
||||
super.readSave(buffer);
|
||||
|
||||
super.readSave(buffer, version());
|
||||
|
||||
name = TypeIO.readStringData(buffer);
|
||||
byte bools = buffer.readByte();
|
||||
isAdmin = (bools & 1) != 0;
|
||||
|
||||
@@ -124,7 +124,7 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
public void read(DataInput stream) throws IOException{
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
health = stream.readUnsignedShort();
|
||||
byte tr = stream.readByte();
|
||||
byte team = Pack.leftByte(tr);
|
||||
@@ -139,6 +139,11 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
|
||||
if(cons != null) cons.read(stream);
|
||||
}
|
||||
|
||||
/** Returns the version of this TileEntity IO code.*/
|
||||
public byte version(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean collide(Bullet other){
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSave(DataInput stream) throws IOException{
|
||||
public void readSave(DataInput stream, byte version) throws IOException{
|
||||
byte team = stream.readByte();
|
||||
boolean dead = stream.readBoolean();
|
||||
float x = stream.readFloat();
|
||||
@@ -150,7 +150,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
byte itemID = stream.readByte();
|
||||
short itemAmount = stream.readShort();
|
||||
|
||||
this.status.readSave(stream);
|
||||
this.status.readSave(stream, version);
|
||||
this.item.amount = itemAmount;
|
||||
this.item.item = content.item(itemID);
|
||||
this.dead = dead;
|
||||
|
||||
@@ -130,7 +130,7 @@ public class Statuses implements Saveable{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSave(DataInput stream) throws IOException{
|
||||
public void readSave(DataInput stream, byte version) throws IOException{
|
||||
for(StatusEntry effect : statuses){
|
||||
Pools.free(effect);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public class Saves{
|
||||
SaveSlot slot = new SaveSlot(index);
|
||||
saves.add(slot);
|
||||
saveMap.put(slot.index, slot);
|
||||
slot.meta = SaveIO.getData(index);
|
||||
slot.meta = SaveIO.getMeta(index);
|
||||
nextSlot = Math.max(index + 1, nextSlot);
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,7 @@ public class Saves{
|
||||
slot.setName(file.nameWithoutExtension());
|
||||
saves.add(slot);
|
||||
saveMap.put(slot.index, slot);
|
||||
slot.meta = SaveIO.getData(slot.index);
|
||||
slot.meta = SaveIO.getMeta(slot.index);
|
||||
current = slot;
|
||||
saveSlots();
|
||||
return slot;
|
||||
@@ -172,7 +172,7 @@ public class Saves{
|
||||
public void load() throws SaveException{
|
||||
try{
|
||||
SaveIO.loadFromSlot(index);
|
||||
meta = SaveIO.getData(index);
|
||||
meta = SaveIO.getMeta(index);
|
||||
current = this;
|
||||
totalPlaytime = meta.timePlayed;
|
||||
}catch(Exception e){
|
||||
@@ -186,7 +186,7 @@ public class Saves{
|
||||
totalPlaytime = time;
|
||||
|
||||
SaveIO.saveToSlot(index);
|
||||
meta = SaveIO.getData(index);
|
||||
meta = SaveIO.getMeta(index);
|
||||
if(!state.is(State.menu)){
|
||||
current = this;
|
||||
}
|
||||
|
||||
180
core/src/io/anuke/mindustry/io/LegacyMapIO.java
Normal file
180
core/src/io/anuke/mindustry/io/LegacyMapIO.java
Normal file
@@ -0,0 +1,180 @@
|
||||
package io.anuke.mindustry.io;
|
||||
|
||||
import io.anuke.arc.collection.ObjectMap;
|
||||
import io.anuke.arc.files.FileHandle;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.Pixmap;
|
||||
import io.anuke.arc.util.Pack;
|
||||
import io.anuke.arc.util.Structs;
|
||||
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.world.*;
|
||||
import io.anuke.mindustry.world.LegacyColorMapper.LegacyBlock;
|
||||
import io.anuke.mindustry.world.blocks.BlockPart;
|
||||
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;
|
||||
|
||||
/** Map IO for the "old" .mmap format.
|
||||
* Differentiate between legacy maps and new maps by checking the extension (or the header).*/
|
||||
public class LegacyMapIO{
|
||||
|
||||
public static Map readMap(FileHandle file, boolean custom) throws IOException{
|
||||
try(DataInputStream stream = new DataInputStream(file.read(1024))){
|
||||
ObjectMap<String, String> tags = new ObjectMap<>();
|
||||
|
||||
//meta is uncompressed
|
||||
int version = stream.readInt();
|
||||
int build = stream.readInt();
|
||||
short width = stream.readShort(), height = stream.readShort();
|
||||
byte tagAmount = stream.readByte();
|
||||
|
||||
for(int i = 0; i < tagAmount; i++){
|
||||
String name = stream.readUTF();
|
||||
String value = stream.readUTF();
|
||||
tags.put(name, value);
|
||||
}
|
||||
|
||||
return new Map(file, width, height, tags, custom, version, build);
|
||||
}
|
||||
}
|
||||
|
||||
public static void readTiles(Map map, Tile[][] tiles) throws IOException{
|
||||
readTiles(map, (x, y) -> tiles[x][y]);
|
||||
}
|
||||
|
||||
public static void readTiles(Map map, TileProvider tiles) throws IOException{
|
||||
readTiles(map.file, map.width, map.height, tiles);
|
||||
}
|
||||
|
||||
private static void readTiles(FileHandle file, int width, int height, Tile[][] tiles) throws IOException{
|
||||
readTiles(file, width, height, (x, y) -> tiles[x][y]);
|
||||
}
|
||||
|
||||
private static void readTiles(FileHandle file, int width, int height, TileProvider tiles) throws IOException{
|
||||
try(BufferedInputStream input = file.read(bufferSize)){
|
||||
|
||||
//read map
|
||||
{
|
||||
DataInputStream stream = new DataInputStream(input);
|
||||
|
||||
stream.readInt(); //version
|
||||
stream.readInt(); //build
|
||||
stream.readInt(); //width + height
|
||||
byte tagAmount = stream.readByte();
|
||||
|
||||
for(int i = 0; i < tagAmount; i++){
|
||||
stream.readUTF(); //key
|
||||
stream.readUTF(); //val
|
||||
}
|
||||
}
|
||||
|
||||
try(DataInputStream stream = new DataInputStream(new InflaterInputStream(input))){
|
||||
|
||||
try{
|
||||
SaveIO.getSaveWriter().readContentHeader(stream);
|
||||
|
||||
//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 consecutives = stream.readUnsignedByte();
|
||||
|
||||
Tile tile = tiles.get(x, y);
|
||||
tile.setFloor((Floor)content.block(floorid));
|
||||
tile.setOverlay(content.block(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));
|
||||
}
|
||||
|
||||
i += consecutives;
|
||||
}
|
||||
|
||||
//read blocks
|
||||
for(int i = 0; i < width * height; i++){
|
||||
int x = i % width, y = i / width;
|
||||
Block block = content.block(stream.readByte());
|
||||
|
||||
Tile tile = tiles.get(x, y);
|
||||
tile.setBlock(block);
|
||||
|
||||
if(tile.entity != null){
|
||||
byte tr = stream.readByte();
|
||||
stream.readShort(); //read health (which is actually irrelevant)
|
||||
|
||||
byte team = Pack.leftByte(tr);
|
||||
byte rotation = Pack.rightByte(tr);
|
||||
|
||||
tile.setTeam(Team.all[team]);
|
||||
tile.entity.health = tile.block().health;
|
||||
tile.rotation(rotation);
|
||||
|
||||
if(tile.block() == Blocks.liquidSource || tile.block() == Blocks.unloader || tile.block() == Blocks.sorter){
|
||||
stream.readByte(); //these blocks have an extra config byte, read it
|
||||
}
|
||||
}else{ //no entity/part, read consecutives
|
||||
int consecutives = stream.readUnsignedByte();
|
||||
|
||||
for(int j = i + 1; j < i + 1 + consecutives; j++){
|
||||
int newx = j % width, newy = j / width;
|
||||
tiles.get(newx, newy).setBlock(block);
|
||||
}
|
||||
|
||||
i += consecutives;
|
||||
}
|
||||
}
|
||||
|
||||
}finally{
|
||||
content.setTemporaryMapper(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Reads a pixmap in the 3.5 pixmap format. */
|
||||
public static void readLegacyPixmap(Pixmap pixmap, Tile[][] tiles){
|
||||
for(int x = 0; x < pixmap.getWidth(); x++){
|
||||
for(int y = 0; y < pixmap.getHeight(); y++){
|
||||
int color = pixmap.getPixel(x, pixmap.getHeight() - 1 - y);
|
||||
LegacyBlock block = LegacyColorMapper.get(color);
|
||||
Tile tile = tiles[x][y];
|
||||
|
||||
tile.setFloor(block.floor);
|
||||
tile.setBlock(block.wall);
|
||||
if(block.ore != null) tile.setOverlay(block.ore);
|
||||
|
||||
//place core
|
||||
if(color == Color.rgba8888(Color.GREEN)){
|
||||
for(int dx = 0; dx < 3; dx++){
|
||||
for(int dy = 0; dy < 3; dy++){
|
||||
int worldx = dx - 1 + x;
|
||||
int worldy = dy - 1 + y;
|
||||
|
||||
//multiblock parts
|
||||
if(Structs.inBounds(worldx, worldy, pixmap.getWidth(), pixmap.getHeight())){
|
||||
Tile write = tiles[worldx][worldy];
|
||||
write.setBlock(BlockPart.get(dx - 1, dy - 1));
|
||||
write.setTeam(Team.blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//actual core parts
|
||||
tile.setBlock(Blocks.coreShard);
|
||||
tile.setTeam(Team.blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,21 @@
|
||||
package io.anuke.mindustry.io;
|
||||
|
||||
import io.anuke.arc.collection.ObjectMap;
|
||||
import io.anuke.arc.collection.ObjectMap.Entry;
|
||||
import io.anuke.arc.files.FileHandle;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.Pixmap;
|
||||
import io.anuke.arc.graphics.Pixmap.Format;
|
||||
import io.anuke.arc.util.*;
|
||||
import io.anuke.arc.util.Time;
|
||||
import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.maps.Map;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.LegacyColorMapper.LegacyBlock;
|
||||
import io.anuke.mindustry.world.blocks.BlockPart;
|
||||
import io.anuke.mindustry.world.blocks.Floor;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
import static io.anuke.mindustry.Vars.bufferSize;
|
||||
import static io.anuke.mindustry.Vars.content;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/** Reads and writes map files. */
|
||||
public class MapIO{
|
||||
public static final int version = 1;
|
||||
private static final int[] pngHeader = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
|
||||
|
||||
public static boolean isImage(FileHandle file){
|
||||
@@ -53,9 +44,9 @@ public class MapIO{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOverlayID(byte b){
|
||||
if(b != 0)
|
||||
floors.drawPixel(x, floors.getHeight() - 1 - y, colorFor(floor(), Blocks.air, content.block(b), getTeam()));
|
||||
public void setOverlay(Block type){
|
||||
if(type != Blocks.air)
|
||||
floors.drawPixel(x, floors.getHeight() - 1 - y, colorFor(floor(), Blocks.air, type, getTeam()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -68,13 +59,10 @@ public class MapIO{
|
||||
}
|
||||
}
|
||||
};
|
||||
readTiles(map, (x, y) -> {
|
||||
tile.x = (short)x;
|
||||
tile.y = (short)y;
|
||||
return tile;
|
||||
});
|
||||
|
||||
floors.drawPixmap(walls, 0, 0);
|
||||
walls.dispose();
|
||||
//TODO actually generate the preview
|
||||
return floors;
|
||||
}
|
||||
|
||||
@@ -96,255 +84,6 @@ public class MapIO{
|
||||
return Color.rgba8888(wall.solid ? wall.color : ore == Blocks.air ? floor.color : ore.color);
|
||||
}
|
||||
|
||||
public static void writeMap(FileHandle file, Map map, Tile[][] tiles) throws IOException{
|
||||
OutputStream output = file.write(false, bufferSize);
|
||||
|
||||
{
|
||||
DataOutputStream stream = new DataOutputStream(output);
|
||||
stream.writeInt(version);
|
||||
stream.writeInt(Version.build);
|
||||
stream.writeShort(tiles.length);
|
||||
stream.writeShort(tiles[0].length);
|
||||
stream.writeByte((byte)map.tags.size);
|
||||
|
||||
for(Entry<String, String> entry : map.tags.entries()){
|
||||
stream.writeUTF(entry.key);
|
||||
stream.writeUTF(entry.value);
|
||||
}
|
||||
}
|
||||
|
||||
try(DataOutputStream stream = new DataOutputStream(new DeflaterOutputStream(output))){
|
||||
int width = map.width, height = map.height;
|
||||
|
||||
SaveIO.getSaveWriter().writeContentHeader(stream);
|
||||
|
||||
//floor first
|
||||
for(int i = 0; i < tiles.length * tiles[0].length; i++){
|
||||
Tile tile = tiles[i % width][i / width];
|
||||
stream.writeByte(tile.getFloorID());
|
||||
stream.writeByte(tile.overlayID());
|
||||
int consecutives = 0;
|
||||
|
||||
for(int j = i + 1; j < width * height && consecutives < 255; j++){
|
||||
Tile nextTile = tiles[j % width][j / width];
|
||||
|
||||
if(nextTile.getFloorID() != tile.getFloorID() || nextTile.block() != Blocks.air || nextTile.overlayID() != tile.overlayID()){
|
||||
break;
|
||||
}
|
||||
|
||||
consecutives++;
|
||||
}
|
||||
|
||||
stream.writeByte(consecutives);
|
||||
i += consecutives;
|
||||
}
|
||||
|
||||
//then blocks
|
||||
for(int i = 0; i < tiles.length * tiles[0].length; i++){
|
||||
Tile tile = tiles[i % width][i / width];
|
||||
stream.writeByte(tile.getBlockID());
|
||||
|
||||
if(tile.block() instanceof BlockPart){
|
||||
stream.writeByte(tile.getLinkByte());
|
||||
}else if(tile.entity != null){
|
||||
stream.writeByte(Pack.byteByte(tile.getTeamID(), tile.rotation())); //team + rotation
|
||||
stream.writeShort(/*(short)tile.entity.health*/tile.block().health); //health
|
||||
if(tile.block() == Blocks.liquidSource || tile.block() == Blocks.unloader || tile.block() == Blocks.sorter){
|
||||
stream.writeByte(-1); //write an meaningless byte here, just a fallback thing
|
||||
}
|
||||
}else{
|
||||
//write consecutive non-entity blocks
|
||||
int consecutives = 0;
|
||||
|
||||
for(int j = i + 1; j < width * height && consecutives < 255; j++){
|
||||
Tile nextTile = tiles[j % width][j / width];
|
||||
|
||||
if(nextTile.block() != tile.block()){
|
||||
break;
|
||||
}
|
||||
|
||||
consecutives++;
|
||||
}
|
||||
|
||||
stream.writeByte(consecutives);
|
||||
i += consecutives;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Map readMap(FileHandle file, boolean custom) throws IOException{
|
||||
try(DataInputStream stream = new DataInputStream(file.read(1024))){
|
||||
ObjectMap<String, String> tags = new ObjectMap<>();
|
||||
|
||||
//meta is uncompressed
|
||||
int version = stream.readInt();
|
||||
if(version == 0){
|
||||
return readLegacyMap(file, custom);
|
||||
}
|
||||
int build = stream.readInt();
|
||||
short width = stream.readShort(), height = stream.readShort();
|
||||
byte tagAmount = stream.readByte();
|
||||
|
||||
for(int i = 0; i < tagAmount; i++){
|
||||
String name = stream.readUTF();
|
||||
String value = stream.readUTF();
|
||||
tags.put(name, value);
|
||||
}
|
||||
|
||||
return new Map(file, width, height, tags, custom, version, build);
|
||||
}
|
||||
}
|
||||
|
||||
/** Reads tiles from a map, version-agnostic. */
|
||||
public static void readTiles(Map map, Tile[][] tiles) throws IOException{
|
||||
readTiles(map, (x, y) -> tiles[x][y]);
|
||||
}
|
||||
|
||||
/** Reads tiles from a map, version-agnostic. */
|
||||
public static void readTiles(Map map, TileProvider tiles) throws IOException{
|
||||
if(map.version == 0){
|
||||
readLegacyMmapTiles(map.file, tiles);
|
||||
}else if(map.version == version){
|
||||
readTiles(map.file, map.width, map.height, tiles);
|
||||
}else{
|
||||
throw new IOException("Unknown map version. What?");
|
||||
}
|
||||
}
|
||||
|
||||
/** Reads tiles from a map in the new build-65 format. */
|
||||
private static void readTiles(FileHandle file, int width, int height, Tile[][] tiles) throws IOException{
|
||||
readTiles(file, width, height, (x, y) -> tiles[x][y]);
|
||||
}
|
||||
|
||||
/** Reads tiles from a map in the new build-65 format. */
|
||||
private static void readTiles(FileHandle file, int width, int height, TileProvider tiles) throws IOException{
|
||||
try(BufferedInputStream input = file.read(bufferSize)){
|
||||
|
||||
//read map
|
||||
{
|
||||
DataInputStream stream = new DataInputStream(input);
|
||||
|
||||
stream.readInt(); //version
|
||||
stream.readInt(); //build
|
||||
stream.readInt(); //width + height
|
||||
byte tagAmount = stream.readByte();
|
||||
|
||||
for(int i = 0; i < tagAmount; i++){
|
||||
stream.readUTF(); //key
|
||||
stream.readUTF(); //val
|
||||
}
|
||||
}
|
||||
|
||||
try(DataInputStream stream = new DataInputStream(new InflaterInputStream(input))){
|
||||
|
||||
MappableContent[][] c = SaveIO.getSaveWriter().readContentHeader(stream);
|
||||
|
||||
try{
|
||||
content.setTemporaryMapper(c);
|
||||
|
||||
//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 consecutives = stream.readUnsignedByte();
|
||||
|
||||
Tile tile = tiles.get(x, y);
|
||||
tile.setFloor((Floor)content.block(floorid));
|
||||
tile.setOverlay(content.block(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));
|
||||
}
|
||||
|
||||
i += consecutives;
|
||||
}
|
||||
|
||||
//read blocks
|
||||
for(int i = 0; i < width * height; i++){
|
||||
int x = i % width, y = i / width;
|
||||
Block block = content.block(stream.readByte());
|
||||
|
||||
Tile tile = tiles.get(x, y);
|
||||
tile.setBlock(block);
|
||||
|
||||
if(tile.entity != null){
|
||||
byte tr = stream.readByte();
|
||||
short health = stream.readShort();
|
||||
|
||||
byte team = Pack.leftByte(tr);
|
||||
byte rotation = Pack.rightByte(tr);
|
||||
|
||||
tile.setTeam(Team.all[team]);
|
||||
tile.entity.health = /*health*/tile.block().health;
|
||||
tile.rotation(rotation);
|
||||
|
||||
if(tile.block() == Blocks.liquidSource || tile.block() == Blocks.unloader || tile.block() == Blocks.sorter){
|
||||
stream.readByte(); //these blocks have an extra config byte, read it
|
||||
}
|
||||
}else{ //no entity/part, read consecutives
|
||||
int consecutives = stream.readUnsignedByte();
|
||||
|
||||
for(int j = i + 1; j < i + 1 + consecutives; j++){
|
||||
int newx = j % width, newy = j / width;
|
||||
tiles.get(newx, newy).setBlock(block);
|
||||
}
|
||||
|
||||
i += consecutives;
|
||||
}
|
||||
}
|
||||
|
||||
}finally{
|
||||
content.setTemporaryMapper(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//region legacy IO
|
||||
|
||||
/** Reads a pixmap in the 3.5 pixmap format. */
|
||||
public static void readLegacyPixmap(Pixmap pixmap, Tile[][] tiles){
|
||||
for(int x = 0; x < pixmap.getWidth(); x++){
|
||||
for(int y = 0; y < pixmap.getHeight(); y++){
|
||||
int color = pixmap.getPixel(x, pixmap.getHeight() - 1 - y);
|
||||
LegacyBlock block = LegacyColorMapper.get(color);
|
||||
Tile tile = tiles[x][y];
|
||||
|
||||
tile.setFloor(block.floor);
|
||||
tile.setBlock(block.wall);
|
||||
if(block.ore != null) tile.setOverlay(block.ore);
|
||||
|
||||
//place core
|
||||
if(color == Color.rgba8888(Color.GREEN)){
|
||||
for(int dx = 0; dx < 3; dx++){
|
||||
for(int dy = 0; dy < 3; dy++){
|
||||
int worldx = dx - 1 + x;
|
||||
int worldy = dy - 1 + y;
|
||||
|
||||
//multiblock parts
|
||||
if(Structs.inBounds(worldx, worldy, pixmap.getWidth(), pixmap.getHeight())){
|
||||
Tile write = tiles[worldx][worldy];
|
||||
write.setBlock(BlockPart.get(dx - 1, dy - 1));
|
||||
write.setTeam(Team.blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//actual core parts
|
||||
tile.setBlock(Blocks.coreShard);
|
||||
tile.setTeam(Team.blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
interface TileProvider{
|
||||
Tile get(int x, int y);
|
||||
}
|
||||
|
||||
@@ -8,25 +8,11 @@ import io.anuke.arc.util.io.ReusableByteOutStream;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* - version of format: int
|
||||
* (begin deflating)
|
||||
* - regions begin
|
||||
* 1. meta tags (short length, key-val UTF pairs)
|
||||
* 2. map data
|
||||
*/
|
||||
public abstract class SaveFileVersion{
|
||||
public final int version;
|
||||
|
||||
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");
|
||||
|
||||
public SaveFileVersion(int version){
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
protected void region(String name, DataInput stream, CounterInputStream counter, IORunner<DataInput> cons) throws IOException{
|
||||
int length;
|
||||
try{
|
||||
@@ -62,10 +48,10 @@ public abstract class SaveFileVersion{
|
||||
if(!isByte){
|
||||
output.writeInt(length);
|
||||
}else{
|
||||
if(length > 255){
|
||||
throw new IOException("Byte write length exceeded: " + length + " > 255");
|
||||
if(length > Short.MAX_VALUE){
|
||||
throw new IOException("Byte write length exceeded: " + length + " > " + Short.MAX_VALUE);
|
||||
}
|
||||
output.writeByte(length);
|
||||
output.writeShort(length);
|
||||
}
|
||||
output.write(byteOutput.getBytes(), 0, length);
|
||||
}
|
||||
@@ -76,7 +62,7 @@ public abstract class SaveFileVersion{
|
||||
|
||||
/** Reads a chunk of some length. Use the runner for reading to catch more descriptive errors. */
|
||||
public int readChunk(DataInput input, boolean isByte, IORunner<DataInput> runner) throws IOException{
|
||||
int length = isByte ? input.readUnsignedByte() : input.readInt();
|
||||
int length = isByte ? input.readUnsignedShort() : input.readInt();
|
||||
runner.accept(input);
|
||||
return length;
|
||||
}
|
||||
@@ -17,16 +17,16 @@ import static io.anuke.mindustry.Vars.*;
|
||||
public class SaveIO{
|
||||
/** Format header. This is the string 'MSAV' in ASCII. */
|
||||
public static final byte[] header = {77, 83, 65, 86};
|
||||
public static final IntMap<SaveFileVersion> versions = new IntMap<>();
|
||||
public static final Array<SaveFileVersion> versionArray = Array.with(new Save1());
|
||||
public static final IntMap<SaveVersion> versions = new IntMap<>();
|
||||
public static final Array<SaveVersion> versionArray = Array.with(new Save1());
|
||||
|
||||
static{
|
||||
for(SaveFileVersion version : versionArray){
|
||||
for(SaveVersion version : versionArray){
|
||||
versions.put(version.version, version);
|
||||
}
|
||||
}
|
||||
|
||||
public static SaveFileVersion getSaveWriter(){
|
||||
public static SaveVersion getSaveWriter(){
|
||||
return versionArray.peek();
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ public class SaveIO{
|
||||
|
||||
public static boolean isSaveValid(DataInputStream stream){
|
||||
try{
|
||||
getData(stream);
|
||||
getMeta(stream);
|
||||
return true;
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
@@ -72,15 +72,15 @@ public class SaveIO{
|
||||
}
|
||||
}
|
||||
|
||||
public static SaveMeta getData(int slot){
|
||||
return getData(getSlotStream(slot));
|
||||
public static SaveMeta getMeta(int slot){
|
||||
return getMeta(getSlotStream(slot));
|
||||
}
|
||||
|
||||
public static SaveMeta getData(DataInputStream stream){
|
||||
public static SaveMeta getMeta(DataInputStream stream){
|
||||
|
||||
try{
|
||||
int version = stream.readInt();
|
||||
SaveMeta meta = versions.get(version).getData(stream);
|
||||
SaveMeta meta = versions.get(version).getMeta(stream);
|
||||
stream.close();
|
||||
return meta;
|
||||
}catch(IOException e){
|
||||
@@ -141,7 +141,7 @@ public class SaveIO{
|
||||
logic.reset();
|
||||
readHeader(stream);
|
||||
int version = stream.readInt();
|
||||
SaveFileVersion ver = versions.get(version);
|
||||
SaveVersion ver = versions.get(version);
|
||||
|
||||
ver.read(stream, counter);
|
||||
}catch(Exception e){
|
||||
@@ -151,7 +151,7 @@ public class SaveIO{
|
||||
}
|
||||
}
|
||||
|
||||
public static SaveFileVersion getVersion(){
|
||||
public static SaveVersion getVersion(){
|
||||
return versionArray.peek();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import io.anuke.mindustry.maps.Map;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
//TODO consider removing and replacing with a raw StringMap
|
||||
public class SaveMeta{
|
||||
public int version;
|
||||
public int build;
|
||||
|
||||
@@ -8,6 +8,7 @@ import io.anuke.mindustry.entities.Entities;
|
||||
import io.anuke.mindustry.entities.EntityGroup;
|
||||
import io.anuke.mindustry.entities.traits.*;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.Serialization;
|
||||
import io.anuke.mindustry.type.ContentType;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
@@ -16,10 +17,17 @@ import java.io.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public abstract class BaseSaveVersion extends SaveFileVersion{
|
||||
public abstract class SaveVersion extends SaveFileReader{
|
||||
public final int version;
|
||||
|
||||
public BaseSaveVersion(int version){
|
||||
super(version);
|
||||
public SaveVersion(int version){
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public SaveMeta getMeta(DataInput stream) throws IOException{
|
||||
stream.readInt(); //length of data, doesn't matter here
|
||||
StringMap map = readStringMap(stream);
|
||||
return new SaveMeta(map.getInt("version"), map.getLong("saved"), map.getLong("playtime"), map.getInt("build"), map.get("mapname"), map.getInt("wave"), Serialization.readRulesStringJson(map.get("rules", "{}")));
|
||||
}
|
||||
|
||||
public void write(DataOutputStream stream) throws IOException{
|
||||
@@ -36,6 +44,28 @@ public abstract class BaseSaveVersion extends SaveFileVersion{
|
||||
region("entities", stream, counter, this::readEntities);
|
||||
}
|
||||
|
||||
public void writeMeta(DataOutput stream) throws IOException{
|
||||
writeStringMap(stream, StringMap.of(
|
||||
"saved", Time.millis(),
|
||||
"playtime", headless ? 0 : control.saves.getTotalPlaytime(),
|
||||
"build", Version.build,
|
||||
"mapname", world.getMap().name(),
|
||||
"wave", state.wave,
|
||||
"wavetime", state.wavetime,
|
||||
"stats", Serialization.writeStatsJson(state.stats),
|
||||
"rules", Serialization.writeRulesJson(state.rules)
|
||||
));
|
||||
}
|
||||
|
||||
public void readMeta(DataInput stream) throws IOException{
|
||||
StringMap map = readStringMap(stream);
|
||||
|
||||
state.wave = map.getInt("wave");
|
||||
state.wavetime = map.getFloat("wavetime", state.rules.waveSpacing);
|
||||
state.stats = Serialization.readStatsStringJson(map.get("stats", "{}"));
|
||||
state.rules = Serialization.readRulesStringJson(map.get("rules", "{}"));
|
||||
}
|
||||
|
||||
public void writeMap(DataOutput stream) throws IOException{
|
||||
//write world size
|
||||
stream.writeShort(world.width());
|
||||
@@ -68,8 +98,10 @@ public abstract class BaseSaveVersion extends SaveFileVersion{
|
||||
stream.writeShort(tile.blockID());
|
||||
|
||||
if(tile.entity != null){
|
||||
//TODO chunks, backward compat
|
||||
tile.entity.write(stream);
|
||||
writeChunk(stream, true, out -> {
|
||||
out.writeByte(tile.entity.version());
|
||||
tile.entity.write(stream);
|
||||
});
|
||||
}else{
|
||||
//write consecutive non-entity blocks
|
||||
int consecutives = 0;
|
||||
@@ -123,8 +155,10 @@ public abstract class BaseSaveVersion extends SaveFileVersion{
|
||||
tile.setBlock(block);
|
||||
|
||||
if(tile.entity != null){
|
||||
//TODO chunks, backward compat
|
||||
tile.entity.read(stream);
|
||||
readChunk(stream, true, in -> {
|
||||
byte version = in.readByte();
|
||||
tile.entity.read(stream, version);
|
||||
});
|
||||
}else{
|
||||
int consecutives = stream.readUnsignedByte();
|
||||
|
||||
@@ -157,11 +191,12 @@ public abstract class BaseSaveVersion extends SaveFileVersion{
|
||||
if(!group.isEmpty() && group.all().get(0) instanceof SaveTrait){
|
||||
stream.writeInt(group.size());
|
||||
for(Entity entity : group.all()){
|
||||
//TODO chunks, backward compat
|
||||
SaveTrait save = (SaveTrait)entity;
|
||||
//each entity is a separate chunk.
|
||||
writeChunk(stream, true, out -> {
|
||||
stream.writeByte(((SaveTrait)entity).getTypeID());
|
||||
((SaveTrait)entity).writeSave(out);
|
||||
stream.writeByte(save.getTypeID());
|
||||
stream.writeByte(save.version());
|
||||
save.writeSave(out);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -174,10 +209,13 @@ public abstract class BaseSaveVersion extends SaveFileVersion{
|
||||
for(int i = 0; i < groups; i++){
|
||||
int amount = stream.readInt();
|
||||
for(int j = 0; j < amount; j++){
|
||||
//TODO chunks, backwards compat
|
||||
byte typeid = stream.readByte();
|
||||
SaveTrait trait = (SaveTrait)TypeTrait.getTypeByID(typeid).get();
|
||||
trait.readSave(stream);
|
||||
//TODO throw exception on read fail
|
||||
readChunk(stream, true, in -> {
|
||||
byte typeid = stream.readByte();
|
||||
byte version = stream.readByte();
|
||||
SaveTrait trait = (SaveTrait)TypeTrait.getTypeByID(typeid).get();
|
||||
trait.readSave(stream, version);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,26 +261,4 @@ public abstract class BaseSaveVersion extends SaveFileVersion{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void writeMeta(DataOutput stream) throws IOException{
|
||||
writeStringMap(stream, StringMap.of(
|
||||
"saved", Time.millis(),
|
||||
"playtime", headless ? 0 : control.saves.getTotalPlaytime(),
|
||||
"build", Version.build,
|
||||
"mapname", world.getMap().name(),
|
||||
"wave", state.wave,
|
||||
"wavetime", state.wavetime//,
|
||||
//"stats", Serialization.writeStatsStreamJson(state.stats),
|
||||
//"rules", Serialization.writeRulesStreamJson(state.rules),
|
||||
));
|
||||
}
|
||||
|
||||
public void readMeta(DataInput stream) throws IOException{
|
||||
StringMap map = readStringMap(stream);
|
||||
|
||||
//TODO read rules, stats
|
||||
|
||||
state.wave = map.getInt("wave");
|
||||
state.wavetime = map.getFloat("wavetime", state.rules.waveSpacing);
|
||||
}
|
||||
}
|
||||
@@ -1,83 +1,10 @@
|
||||
package io.anuke.mindustry.io.versions;
|
||||
|
||||
import io.anuke.arc.collection.ObjectMap;
|
||||
import io.anuke.arc.util.Time;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.gen.Serialization;
|
||||
import io.anuke.mindustry.io.SaveFileVersion;
|
||||
import io.anuke.mindustry.maps.Map;
|
||||
import io.anuke.mindustry.type.ContentType;
|
||||
import io.anuke.mindustry.type.Zone;
|
||||
import io.anuke.mindustry.io.SaveVersion;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Save1 extends SaveFileVersion{
|
||||
public class Save1 extends SaveVersion{
|
||||
|
||||
public Save1(){
|
||||
super(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInputStream stream) throws IOException{
|
||||
stream.readLong(); //time
|
||||
stream.readLong(); //total playtime
|
||||
stream.readInt(); //build
|
||||
|
||||
//general state
|
||||
state.rules = Serialization.readRulesStreamJson(stream);
|
||||
String mapname = stream.readUTF();
|
||||
Map map = world.maps.all().find(m -> m.name().equals(mapname));
|
||||
if(map == null) map = new Map(customMapDirectory.child(mapname), 1, 1, new ObjectMap<>(), true);
|
||||
world.setMap(map);
|
||||
state.rules.spawns = map.getWaves();
|
||||
if(content.getByID(ContentType.zone, state.rules.zone) != null){
|
||||
Rules rules = content.<Zone>getByID(ContentType.zone, state.rules.zone).rules.get();
|
||||
if(rules.spawns != DefaultWaves.get()){
|
||||
state.rules.spawns = rules.spawns;
|
||||
}
|
||||
}
|
||||
|
||||
int wave = stream.readInt();
|
||||
float wavetime = stream.readFloat();
|
||||
|
||||
state.wave = wave;
|
||||
state.wavetime = wavetime;
|
||||
state.stats = Serialization.readStats(stream);
|
||||
world.spawner.read(stream);
|
||||
|
||||
content.setTemporaryMapper(readContentHeader(stream));
|
||||
|
||||
readEntities(stream);
|
||||
readMap(stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutputStream stream) throws IOException{
|
||||
//--META--
|
||||
stream.writeInt(version); //version id
|
||||
stream.writeLong(Time.millis()); //last saved
|
||||
stream.writeLong(headless ? 0 : control.saves.getTotalPlaytime()); //playtime
|
||||
stream.writeInt(Version.build); //build
|
||||
|
||||
//--GENERAL STATE--
|
||||
Serialization.writeRulesStreamJson(stream, state.rules);
|
||||
stream.writeUTF(world.getMap().name()); //map name
|
||||
|
||||
stream.writeInt(state.wave); //wave
|
||||
stream.writeFloat(state.wavetime); //wave countdown
|
||||
|
||||
Serialization.writeStats(stream, state.stats);
|
||||
|
||||
world.spawner.write(stream);
|
||||
|
||||
writeContentHeader(stream);
|
||||
|
||||
//--ENTITIES--
|
||||
|
||||
writeEntities(stream);
|
||||
|
||||
writeMap(stream);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import io.anuke.arc.util.Log;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.game.DefaultWaves;
|
||||
import io.anuke.mindustry.game.SpawnGroup;
|
||||
import io.anuke.mindustry.io.MapIO;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
@@ -44,7 +43,11 @@ public class Map implements Comparable<Map>{
|
||||
}
|
||||
|
||||
public Map(FileHandle file, int width, int height, ObjectMap<String, String> tags, boolean custom){
|
||||
this(file, width, height, tags, custom, MapIO.version);
|
||||
this(file, width, height, tags, custom, -1);
|
||||
}
|
||||
|
||||
public Map(ObjectMap<String, String> tags){
|
||||
this(Vars.customMapDirectory.child(tags.get("name", "unknown")), 0, 0, tags, true);
|
||||
}
|
||||
|
||||
public Array<SpawnGroup> getWaves(){
|
||||
|
||||
@@ -6,7 +6,6 @@ import io.anuke.mindustry.maps.Map;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
import static io.anuke.mindustry.Vars.customMapDirectory;
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
public abstract class RandomGenerator extends Generator{
|
||||
@@ -32,7 +31,7 @@ public abstract class RandomGenerator extends Generator{
|
||||
|
||||
decorate(tiles);
|
||||
|
||||
world.setMap(new Map(customMapDirectory.child("generated"), 0, 0, new ObjectMap<>(), true));
|
||||
world.setMap(new Map(new ObjectMap<>()));
|
||||
}
|
||||
|
||||
public abstract void decorate(Tile[][] tiles);
|
||||
|
||||
@@ -14,7 +14,8 @@ public class Administration{
|
||||
|
||||
public Administration(){
|
||||
Core.settings.defaults(
|
||||
"strict", true
|
||||
"strict", true,
|
||||
"servername", "Server"
|
||||
);
|
||||
|
||||
load();
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
package io.anuke.mindustry.net;
|
||||
|
||||
import io.anuke.arc.collection.ObjectMap;
|
||||
import io.anuke.arc.collection.ObjectMap.Entry;
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.util.Time;
|
||||
import io.anuke.mindustry.entities.Entities;
|
||||
import io.anuke.mindustry.entities.type.Player;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.game.Teams.TeamData;
|
||||
import io.anuke.mindustry.game.Version;
|
||||
import io.anuke.mindustry.gen.Serialization;
|
||||
import io.anuke.mindustry.io.SaveIO;
|
||||
import io.anuke.mindustry.maps.Map;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
@@ -22,44 +20,16 @@ public class NetworkIO{
|
||||
public static void writeWorld(Player player, OutputStream os){
|
||||
|
||||
try(DataOutputStream stream = new DataOutputStream(os)){
|
||||
//--GENERAL STATE--
|
||||
Serialization.writeRules(stream, state.rules);
|
||||
stream.writeUTF(world.getMap().name()); //map name
|
||||
SaveIO.getSaveWriter().writeStringMap(stream, world.getMap().tags);
|
||||
|
||||
//write tags
|
||||
ObjectMap<String, String> tags = world.getMap().tags;
|
||||
stream.writeByte(tags.size);
|
||||
for(Entry<String, String> entry : tags.entries()){
|
||||
stream.writeUTF(entry.key);
|
||||
stream.writeUTF(entry.value);
|
||||
}
|
||||
|
||||
stream.writeInt(state.wave); //wave
|
||||
stream.writeFloat(state.wavetime); //wave countdown
|
||||
stream.writeInt(state.wave);
|
||||
stream.writeFloat(state.wavetime);
|
||||
|
||||
stream.writeInt(player.id);
|
||||
player.write(stream);
|
||||
|
||||
SaveIO.getSaveWriter().writeMap(stream);
|
||||
|
||||
stream.write(Team.all.length);
|
||||
|
||||
//write team data
|
||||
for(Team team : Team.all){
|
||||
TeamData data = state.teams.get(team);
|
||||
stream.writeByte(team.ordinal());
|
||||
|
||||
stream.writeByte(data.enemies.size());
|
||||
for(Team enemy : data.enemies){
|
||||
stream.writeByte(enemy.ordinal());
|
||||
}
|
||||
|
||||
stream.writeByte(data.cores.size);
|
||||
for(Tile tile : data.cores){
|
||||
stream.writeInt(tile.pos());
|
||||
}
|
||||
}
|
||||
|
||||
}catch(IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -69,25 +39,11 @@ public class NetworkIO{
|
||||
|
||||
try(DataInputStream stream = new DataInputStream(is)){
|
||||
Time.clear();
|
||||
|
||||
//general state
|
||||
state.rules = Serialization.readRules(stream);
|
||||
String map = stream.readUTF();
|
||||
world.setMap(new Map(SaveIO.getSaveWriter().readStringMap(stream)));
|
||||
|
||||
ObjectMap<String, String> tags = new ObjectMap<>();
|
||||
|
||||
byte tagSize = stream.readByte();
|
||||
for(int i = 0; i < tagSize; i++){
|
||||
String key = stream.readUTF();
|
||||
String value = stream.readUTF();
|
||||
tags.put(key, value);
|
||||
}
|
||||
|
||||
int wave = stream.readInt();
|
||||
float wavetime = stream.readFloat();
|
||||
|
||||
state.wave = wave;
|
||||
state.wavetime = wavetime;
|
||||
state.wave = stream.readInt();
|
||||
state.wavetime = stream.readFloat();
|
||||
|
||||
Entities.clear();
|
||||
int id = stream.readInt();
|
||||
@@ -96,81 +52,60 @@ public class NetworkIO{
|
||||
player.resetID(id);
|
||||
player.add();
|
||||
|
||||
//map
|
||||
SaveIO.getSaveWriter().readMap(stream);
|
||||
world.setMap(new Map(customMapDirectory.child(map), 0, 0, new ObjectMap<>(), true));
|
||||
|
||||
state.teams = new Teams();
|
||||
|
||||
byte teams = stream.readByte();
|
||||
for(int i = 0; i < teams; i++){
|
||||
Team team = Team.all[stream.readByte()];
|
||||
|
||||
byte enemies = stream.readByte();
|
||||
Team[] enemyArr = new Team[enemies];
|
||||
for(int j = 0; j < enemies; j++){
|
||||
enemyArr[j] = Team.all[stream.readByte()];
|
||||
}
|
||||
|
||||
state.teams.add(team, enemyArr);
|
||||
|
||||
byte cores = stream.readByte();
|
||||
|
||||
for(int j = 0; j < cores; j++){
|
||||
state.teams.get(team).cores.add(world.tile(stream.readInt()));
|
||||
}
|
||||
}
|
||||
|
||||
}catch(IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static ByteBuffer writeServerData(){
|
||||
int maxlen = 32;
|
||||
|
||||
String host = (headless ? "Server" : player.name);
|
||||
String name = (headless ? Core.settings.getString("servername") : player.name);
|
||||
String map = world.getMap() == null ? "None" : world.getMap().name();
|
||||
|
||||
host = host.substring(0, Math.min(host.length(), maxlen));
|
||||
map = map.substring(0, Math.min(map.length(), maxlen));
|
||||
ByteBuffer buffer = ByteBuffer.allocate(256);
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.allocate(128);
|
||||
|
||||
buffer.put((byte)host.getBytes(charset).length);
|
||||
buffer.put(host.getBytes(charset));
|
||||
|
||||
buffer.put((byte)map.getBytes(charset).length);
|
||||
buffer.put(map.getBytes(charset));
|
||||
writeString(buffer, name, 100);
|
||||
writeString(buffer, map);
|
||||
|
||||
buffer.putInt(playerGroup.size());
|
||||
buffer.putInt(state.wave);
|
||||
buffer.putInt(Version.build);
|
||||
buffer.put((byte)Version.type.getBytes(charset).length);
|
||||
buffer.put(Version.type.getBytes(charset));
|
||||
writeString(buffer, Version.type);
|
||||
//TODO additional information:
|
||||
// - gamemode ID/name (just pick the closest one?)
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static Host readServerData(String hostAddress, ByteBuffer buffer){
|
||||
byte hlength = buffer.get();
|
||||
byte[] hb = new byte[hlength];
|
||||
buffer.get(hb);
|
||||
|
||||
byte mlength = buffer.get();
|
||||
byte[] mb = new byte[mlength];
|
||||
buffer.get(mb);
|
||||
|
||||
String host = new String(hb, charset);
|
||||
String map = new String(mb, charset);
|
||||
|
||||
String host = readString(buffer);
|
||||
String map = readString(buffer);
|
||||
int players = buffer.getInt();
|
||||
int wave = buffer.getInt();
|
||||
int version = buffer.getInt();
|
||||
byte tlength = buffer.get();
|
||||
byte[] tb = new byte[tlength];
|
||||
buffer.get(tb);
|
||||
String vertype = new String(tb, charset);
|
||||
String vertype = readString(buffer);
|
||||
|
||||
return new Host(host, hostAddress, map, wave, players, version, vertype);
|
||||
}
|
||||
|
||||
private static void writeString(ByteBuffer buffer, String string, int maxlen){
|
||||
byte[] bytes = string.getBytes(charset);
|
||||
//truncating this way may lead to wierd encoding errors at the ends of strings...
|
||||
if(bytes.length > maxlen){
|
||||
bytes = Arrays.copyOfRange(bytes, 0, maxlen);
|
||||
}
|
||||
|
||||
buffer.put((byte)bytes.length);
|
||||
buffer.put(bytes);
|
||||
}
|
||||
|
||||
private static void writeString(ByteBuffer buffer, String string){
|
||||
writeString(buffer, string, 32);
|
||||
}
|
||||
|
||||
private static String readString(ByteBuffer buffer){
|
||||
short length = (short)(buffer.get() & 0xff);
|
||||
byte[] bytes = new byte[length];
|
||||
buffer.get(bytes);
|
||||
return new String(bytes, charset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,9 +164,9 @@ public class JoinDialog extends FloatingDialog{
|
||||
server.content.clear();
|
||||
|
||||
server.content.table(t -> {
|
||||
t.add(versionString).left();
|
||||
t.add("[lightgray]" + host.name).width(targetWidth() - 10f).left().get().setEllipsis(true);
|
||||
t.row();
|
||||
t.add("[lightgray]" + Core.bundle.format("server.hostname", host.name)).width(targetWidth() - 10f).left().get().setEllipsis(true);
|
||||
t.add(versionString).left();
|
||||
t.row();
|
||||
t.add("[lightgray]" + (host.players != 1 ? Core.bundle.format("players", host.players) :
|
||||
Core.bundle.format("players.single", host.players))).left();
|
||||
|
||||
@@ -319,8 +319,8 @@ public class BuildBlock extends Block{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
progress = stream.readFloat();
|
||||
short pid = stream.readShort();
|
||||
short rid = stream.readShort();
|
||||
|
||||
@@ -90,8 +90,8 @@ public class Door extends Wall{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
open = stream.readBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,8 +212,8 @@ public class ForceProjector extends Block{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
broken = stream.readBoolean();
|
||||
buildup = stream.readFloat();
|
||||
radscl = stream.readFloat();
|
||||
|
||||
@@ -154,8 +154,8 @@ public class MendProjector extends Block{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
heat = stream.readFloat();
|
||||
phaseHeat = stream.readFloat();
|
||||
}
|
||||
|
||||
@@ -153,8 +153,8 @@ public class OverdriveProjector extends Block{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
heat = stream.readFloat();
|
||||
phaseHeat = stream.readFloat();
|
||||
}
|
||||
|
||||
@@ -131,8 +131,8 @@ public class ItemTurret extends CooledTurret{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
byte amount = stream.readByte();
|
||||
for(int i = 0; i < amount; i++){
|
||||
Item item = Vars.content.item(stream.readByte());
|
||||
|
||||
@@ -53,8 +53,8 @@ public class BufferedItemBridge extends ExtendingItemBridge{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
buffer.read(stream);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,8 +376,8 @@ public class Conveyor extends Block{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
convey.clear();
|
||||
int amount = stream.readInt();
|
||||
convey.ensureCapacity(Math.min(amount, 10));
|
||||
|
||||
@@ -340,8 +340,8 @@ public class ItemBridge extends Block{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
link = stream.readInt();
|
||||
uptime = stream.readFloat();
|
||||
byte links = stream.readByte();
|
||||
|
||||
@@ -94,8 +94,8 @@ public class Junction extends Block{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
for(Buffer b : buffers){
|
||||
b.read(stream);
|
||||
}
|
||||
|
||||
@@ -328,8 +328,8 @@ public class MassDriver extends Block{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
link = stream.readInt();
|
||||
rotation = stream.readFloat();
|
||||
state = DriverState.values()[stream.readByte()];
|
||||
|
||||
@@ -134,14 +134,13 @@ public class Sorter extends Block{
|
||||
@Override
|
||||
public void write(DataOutput stream) throws IOException{
|
||||
super.write(stream);
|
||||
stream.writeByte(sortItem == null ? -1 : sortItem.id);
|
||||
stream.writeShort(sortItem == null ? -1 : sortItem.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
byte b = stream.readByte();
|
||||
sortItem = b == -1 ? null : content.items().get(b);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
sortItem = content.item(stream.readShort());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,8 +172,8 @@ public class ImpactReactor extends PowerGenerator{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
warmup = stream.readFloat();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,8 +188,8 @@ public class NuclearReactor extends PowerGenerator{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
heat = stream.readFloat();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,8 +68,8 @@ public class PowerGenerator extends PowerDistributor{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
productionEfficiency = stream.readFloat();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,8 +123,8 @@ public class Cultivator extends GenericCrafter{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
warmup = stream.readFloat();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,8 +156,8 @@ public class GenericCrafter extends Block{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
progress = stream.readFloat();
|
||||
warmup = stream.readFloat();
|
||||
}
|
||||
|
||||
@@ -121,8 +121,8 @@ public class LiquidSource extends Block{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
byte id = stream.readByte();
|
||||
source = id == -1 ? null : content.liquid(id);
|
||||
}
|
||||
|
||||
@@ -109,8 +109,8 @@ public class Unloader extends Block{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
byte id = stream.readByte();
|
||||
sortItem = id == -1 ? null : content.items().get(id);
|
||||
}
|
||||
|
||||
@@ -185,8 +185,8 @@ public class MechPad extends Block{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
progress = stream.readFloat();
|
||||
time = stream.readFloat();
|
||||
heat = stream.readFloat();
|
||||
|
||||
@@ -198,13 +198,12 @@ public class UnitFactory extends Block{
|
||||
public void write(DataOutput stream) throws IOException{
|
||||
super.write(stream);
|
||||
stream.writeFloat(buildTime);
|
||||
stream.writeFloat(0f);
|
||||
stream.writeInt(spawned);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
super.read(stream);
|
||||
public void read(DataInput stream, byte revision) throws IOException{
|
||||
super.read(stream, revision);
|
||||
buildTime = stream.readFloat();
|
||||
spawned = stream.readInt();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user