diff --git a/annotations/src/main/java/io/anuke/annotations/Annotations.java b/annotations/src/main/java/io/anuke/annotations/Annotations.java index 7961a57b89..06bde34e2e 100644 --- a/annotations/src/main/java/io/anuke/annotations/Annotations.java +++ b/annotations/src/main/java/io/anuke/annotations/Annotations.java @@ -4,12 +4,27 @@ import java.lang.annotation.*; public class Annotations{ + /** Indicates that a method should always call its super version. */ + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.SOURCE) + public @interface CallSuper{ + + } + + /** Annotation that allows overriding CallSuper annotation. To be used on method that overrides method with CallSuper annotation from parent class.*/ + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.SOURCE) + public @interface OverrideCallSuper { + } + + /** Indicates that a method return or field can be null.*/ @Target({ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.SOURCE) public @interface Nullable{ } + /** Indicates that a method return or field cannot be null.*/ @Target({ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.SOURCE) public @interface NonNull{ diff --git a/annotations/src/main/java/io/anuke/annotations/CallSuperAnnotationProcessor.java b/annotations/src/main/java/io/anuke/annotations/CallSuperAnnotationProcessor.java new file mode 100644 index 0000000000..1bdc75c786 --- /dev/null +++ b/annotations/src/main/java/io/anuke/annotations/CallSuperAnnotationProcessor.java @@ -0,0 +1,65 @@ +package io.anuke.annotations; + +import com.sun.source.util.TreePath; +import com.sun.source.util.Trees; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; +import io.anuke.annotations.Annotations.OverrideCallSuper; + +import javax.annotation.processing.*; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic.Kind; +import java.util.List; +import java.util.Set; + +@SupportedAnnotationTypes("java.lang.Override") +public class CallSuperAnnotationProcessor extends AbstractProcessor{ + private Trees trees; + + @Override + public void init (ProcessingEnvironment pe) { + super.init(pe); + trees = Trees.instance(pe); + } + + public boolean process (Set annotations, RoundEnvironment roundEnv) { + for (Element e : roundEnv.getElementsAnnotatedWith(Override.class)) { + if (e.getAnnotation(OverrideCallSuper.class) != null) return false; + + CodeAnalyzerTreeScanner codeScanner = new CodeAnalyzerTreeScanner(); + codeScanner.setMethodName(e.getSimpleName().toString()); + + TreePath tp = trees.getPath(e.getEnclosingElement()); + codeScanner.scan(tp, trees); + + if (codeScanner.isCallSuperUsed()) { + List list = codeScanner.getMethod().getBody().getStatements(); + + if (!doesCallSuper(list, codeScanner.getMethodName())) { + processingEnv.getMessager().printMessage(Kind.ERROR, "Overriding method '" + codeScanner.getMethodName() + "' must explicitly call super method from its parent class.", e); + } + } + } + + return false; + } + + private boolean doesCallSuper (List list, String methodName) { + for (Object object : list) { + if (object instanceof JCTree.JCExpressionStatement) { + JCTree.JCExpressionStatement expr = (JCExpressionStatement) object; + String exprString = expr.toString(); + if (exprString.startsWith("super." + methodName) && exprString.endsWith(");")) return true; + } + } + + return false; + } + + @Override + public SourceVersion getSupportedSourceVersion () { + return SourceVersion.RELEASE_8; + } +} diff --git a/annotations/src/main/java/io/anuke/annotations/CodeAnalyzerTreeScanner.java b/annotations/src/main/java/io/anuke/annotations/CodeAnalyzerTreeScanner.java new file mode 100644 index 0000000000..c2d543e3be --- /dev/null +++ b/annotations/src/main/java/io/anuke/annotations/CodeAnalyzerTreeScanner.java @@ -0,0 +1,98 @@ +package io.anuke.annotations; + +import com.sun.source.tree.*; +import com.sun.source.util.TreePathScanner; +import com.sun.source.util.Trees; +import com.sun.tools.javac.code.Scope; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.code.Symbol.MethodSymbol; +import com.sun.tools.javac.code.Type.ClassType; +import com.sun.tools.javac.tree.JCTree.JCIdent; +import com.sun.tools.javac.tree.JCTree.JCTypeApply; +import io.anuke.annotations.Annotations.CallSuper; + +import java.lang.annotation.Annotation; + +class CodeAnalyzerTreeScanner extends TreePathScanner { + private String methodName; + private MethodTree method; + private boolean callSuperUsed; + + @Override + public Object visitClass (ClassTree classTree, Trees trees) { + Tree extendTree = classTree.getExtendsClause(); + + if (extendTree instanceof JCTypeApply) { //generic classes case + JCTypeApply generic = (JCTypeApply) extendTree; + extendTree = generic.clazz; + } + + if (extendTree instanceof JCIdent) { + JCIdent tree = (JCIdent) extendTree; + Scope members = tree.sym.members(); + + if (checkScope(members)) + return super.visitClass(classTree, trees); + + if (checkSuperTypes((ClassType) tree.type)) + return super.visitClass(classTree, trees); + + } + callSuperUsed = false; + + return super.visitClass(classTree, trees); + } + + public boolean checkSuperTypes (ClassType type) { + if (type.supertype_field != null && type.supertype_field.tsym != null) { + if (checkScope(type.supertype_field.tsym.members())) + return true; + else + return checkSuperTypes((ClassType) type.supertype_field); + } + + return false; + } + + public boolean checkScope (Scope members) { + for (Symbol s : members.getElements()) { + if (s instanceof MethodSymbol) { + MethodSymbol ms = (MethodSymbol) s; + + if (ms.getSimpleName().toString().equals(methodName)) { + Annotation annotation = ms.getAnnotation(CallSuper.class); + if (annotation != null) { + callSuperUsed = true; + return true; + } + } + } + } + + return false; + } + + @Override + public Object visitMethod (MethodTree methodTree, Trees trees) { + if (methodTree.getName().toString().equals(methodName)) + method = methodTree; + + return super.visitMethod(methodTree, trees); + } + + public void setMethodName (String methodName) { + this.methodName = methodName; + } + + public String getMethodName () { + return methodName; + } + + public MethodTree getMethod () { + return method; + } + + public boolean isCallSuperUsed () { + return callSuperUsed; + } +} \ No newline at end of file diff --git a/annotations/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/annotations/src/main/resources/META-INF/services/javax.annotation.processing.Processor index af76756139..5954bfccb9 100644 --- a/annotations/src/main/resources/META-INF/services/javax.annotation.processing.Processor +++ b/annotations/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -1,3 +1,4 @@ io.anuke.annotations.RemoteMethodAnnotationProcessor io.anuke.annotations.SerializeAnnotationProcessor io.anuke.annotations.StructAnnotationProcessor +io.anuke.annotations.CallSuperAnnotationProcessor \ No newline at end of file diff --git a/build.gradle b/build.gradle index 9f587e2791..415b6fe183 100644 --- a/build.gradle +++ b/build.gradle @@ -177,6 +177,31 @@ project(":core"){ } dependencies{ + if(System.properties["user.name"] == "anuke"){ + task cleanGen{ + doFirst{ + delete{ + delete "../core/src/io/anuke/mindustry/gen/" + } + } + } + + task copyGen{ + doLast{ + copy{ + from("../core/build/generated/sources/annotationProcessor/java/main/io/anuke/mindustry/gen"){ + include "**/*.java" + } + + into "../core/src/io/anuke/mindustry/gen" + } + } + } + + compileJava.dependsOn(cleanGen) + compileJava.finalizedBy(copyGen) + } + compile arcModule("arc-core") compile arcModule("extensions:freetype") compile arcModule("extensions:arcnet") @@ -227,6 +252,7 @@ project(":annotations"){ dependencies{ compile 'com.squareup:javapoet:1.11.0' + compile files("${System.getProperty('java.home')}/../lib/tools.jar") } } diff --git a/core/src/io/anuke/mindustry/core/NetClient.java b/core/src/io/anuke/mindustry/core/NetClient.java index 534d98093c..2cdd52ad3b 100644 --- a/core/src/io/anuke/mindustry/core/NetClient.java +++ b/core/src/io/anuke/mindustry/core/NetClient.java @@ -7,7 +7,7 @@ import io.anuke.arc.collection.IntSet; import io.anuke.arc.graphics.Color; import io.anuke.arc.math.RandomXS128; import io.anuke.arc.util.*; -import io.anuke.arc.util.io.ReusableByteArrayInputStream; +import io.anuke.arc.util.io.ReusableByteInStream; import io.anuke.arc.util.serialization.Base64Coder; import io.anuke.mindustry.Vars; import io.anuke.mindustry.core.GameState.State; @@ -50,7 +50,7 @@ public class NetClient implements ApplicationListener{ /** List of entities that were removed, and need not be added while syncing. */ private IntSet removed = new IntSet(); /** Byte stream for reading in snapshots. */ - private ReusableByteArrayInputStream byteStream = new ReusableByteArrayInputStream(); + private ReusableByteInStream byteStream = new ReusableByteInStream(); private DataInputStream dataStream = new DataInputStream(byteStream); public NetClient(){ diff --git a/core/src/io/anuke/mindustry/core/NetServer.java b/core/src/io/anuke/mindustry/core/NetServer.java index 9c5f6ed830..605dce9128 100644 --- a/core/src/io/anuke/mindustry/core/NetServer.java +++ b/core/src/io/anuke/mindustry/core/NetServer.java @@ -13,7 +13,7 @@ import io.anuke.arc.math.geom.Rectangle; import io.anuke.arc.math.geom.Vector2; import io.anuke.arc.util.*; import io.anuke.arc.util.io.ByteBufferOutput; -import io.anuke.arc.util.io.CountableByteArrayOutputStream; +import io.anuke.arc.util.io.ReusableByteOutStream; import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.entities.Entities; @@ -56,7 +56,7 @@ public class NetServer implements ApplicationListener{ private ByteBufferOutput outputBuffer = new ByteBufferOutput(writeBuffer); /** Stream for writing player sync data to. */ - private CountableByteArrayOutputStream syncStream = new CountableByteArrayOutputStream(); + private ReusableByteOutStream syncStream = new ReusableByteOutStream(); /** Data stream for writing player sync data to. */ private DataOutputStream dataStream = new DataOutputStream(syncStream); diff --git a/core/src/io/anuke/mindustry/entities/type/TileEntity.java b/core/src/io/anuke/mindustry/entities/type/TileEntity.java index 51f84ebefc..1f77fd475b 100644 --- a/core/src/io/anuke/mindustry/entities/type/TileEntity.java +++ b/core/src/io/anuke/mindustry/entities/type/TileEntity.java @@ -1,7 +1,6 @@ package io.anuke.mindustry.entities.type; -import io.anuke.annotations.Annotations.Loc; -import io.anuke.annotations.Annotations.Remote; +import io.anuke.annotations.Annotations.*; import io.anuke.arc.Events; import io.anuke.arc.collection.Array; import io.anuke.arc.collection.ObjectSet; @@ -115,18 +114,14 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{ return dead || tile.entity != this; } + @CallSuper public void write(DataOutput stream) throws IOException{ } - public void writeConfig(DataOutput stream) throws IOException{ - } - + @CallSuper public void read(DataInput stream) throws IOException{ } - public void readConfig(DataInput stream) throws IOException{ - } - public boolean collide(Bullet other){ return true; } diff --git a/core/src/io/anuke/mindustry/io/MapIO.java b/core/src/io/anuke/mindustry/io/MapIO.java index 407438e698..f14525abd3 100644 --- a/core/src/io/anuke/mindustry/io/MapIO.java +++ b/core/src/io/anuke/mindustry/io/MapIO.java @@ -171,7 +171,9 @@ public class MapIO{ }else if(tile.entity != null){ stream.writeByte(Pack.byteByte(tile.getTeamID(), tile.getRotation())); //team + rotation stream.writeShort(/*(short)tile.entity.health*/tile.block().health); //health - tile.entity.writeConfig(stream); + 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; @@ -305,7 +307,9 @@ public class MapIO{ tile.entity.health = /*health*/tile.block().health; tile.setRotation(rotation); - tile.entity.readConfig(stream); + 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(); diff --git a/core/src/io/anuke/mindustry/io/SaveFileVersion.java b/core/src/io/anuke/mindustry/io/SaveFileVersion.java index 48e7cba779..49a14cd24b 100644 --- a/core/src/io/anuke/mindustry/io/SaveFileVersion.java +++ b/core/src/io/anuke/mindustry/io/SaveFileVersion.java @@ -1,8 +1,9 @@ package io.anuke.mindustry.io; -import io.anuke.arc.collection.Array; -import io.anuke.arc.collection.ObjectMap; +import io.anuke.arc.collection.*; +import io.anuke.arc.collection.ObjectMap.Entry; import io.anuke.arc.util.Pack; +import io.anuke.arc.util.io.ReusableByteOutStream; import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.entities.Entities; import io.anuke.mindustry.entities.EntityGroup; @@ -18,8 +19,21 @@ import java.io.*; import static io.anuke.mindustry.Vars.content; import static io.anuke.mindustry.Vars.world; +/** + * Format: + * + * Everything is compressed. Use a DeflaterStream to begin reading. + * + * 1. version of format / int + * 2. meta tags + * - length / short + * - continues with (string, string) pairs indicating key, value + */ public abstract class SaveFileVersion{ public final int version; + + private final ReusableByteOutStream byteOutput = new ReusableByteOutStream(); + private final DataOutputStream dataBytes = new DataOutputStream(byteOutput); private final ObjectMap fallback = ObjectMap.of( "alpha-dart-mech-pad", "dart-mech-pad" ); @@ -28,6 +42,42 @@ public abstract class SaveFileVersion{ this.version = version; } + /** 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 runner) throws IOException{ + //reset output position + byteOutput.position(0); + //writer the needed info + runner.accept(dataBytes); + int length = byteOutput.position(); + //write length (either int or byte) followed by the output bytes + if(!isByte){ + output.writeInt(length); + }else{ + if(length > 255){ + throw new IOException("Byte write length exceeded: " + length + " > 255"); + } + output.writeByte(length); + } + output.write(byteOutput.getBytes(), 0, length); + } + + /** Reads a chunk of some length. Use the runner for reading to catch more descriptive errors. */ + public int readChunk(DataInput input, boolean isByte, IORunner runner) throws IOException{ + int length = isByte ? input.readUnsignedByte() : input.readInt(); + //TODO descriptive error with chunk name + runner.accept(input); + return length; + } + + /** Skip a chunk completely. */ + public void skipChunk(DataInput input, boolean isByte) throws IOException{ + int length = readChunk(input, isByte, t -> {}); + int skipped = input.skipBytes(length); + if(length != skipped){ + throw new IOException("Could not skip bytes. Expected length: " + length + "; Actual length: " + skipped); + } + } + public SaveMeta getData(DataInputStream stream) throws IOException{ long time = stream.readLong(); long playtime = stream.readLong(); @@ -39,6 +89,23 @@ public abstract class SaveFileVersion{ return new SaveMeta(version, time, playtime, build, map, wave, rules); } + public void writeMeta(DataOutputStream stream, ObjectMap map) throws IOException{ + stream.writeShort(map.size); + for(Entry entry : map.entries()){ + stream.writeUTF(entry.key); + stream.writeUTF(entry.value); + } + } + + public StringMap readMeta(DataInputStream stream) throws IOException{ + StringMap map = new StringMap(); + short size = stream.readShort(); + for(int i = 0; i < size; i++){ + map.put(stream.readUTF(), stream.readUTF()); + } + return map; + } + public void writeMap(DataOutputStream stream) throws IOException{ //write world size stream.writeShort(world.width()); @@ -81,7 +148,6 @@ public abstract class SaveFileVersion{ if(tile.entity.liquids != null) tile.entity.liquids.write(stream); if(tile.entity.cons != null) tile.entity.cons.write(stream); - tile.entity.writeConfig(stream); tile.entity.write(stream); }else{ //write consecutive non-entity blocks @@ -157,7 +223,6 @@ public abstract class SaveFileVersion{ if(tile.entity.liquids != null) tile.entity.liquids.read(stream); if(tile.entity.cons != null) tile.entity.cons.read(stream); - tile.entity.readConfig(stream); tile.entity.read(stream); }else{ int consecutives = stream.readUnsignedByte(); @@ -255,4 +320,8 @@ public abstract class SaveFileVersion{ public abstract void read(DataInputStream stream) throws IOException; public abstract void write(DataOutputStream stream) throws IOException; + + public interface IORunner{ + void accept(T stream) throws IOException; + } } diff --git a/core/src/io/anuke/mindustry/io/SaveIO.java b/core/src/io/anuke/mindustry/io/SaveIO.java index ad6231d118..8be9291446 100644 --- a/core/src/io/anuke/mindustry/io/SaveIO.java +++ b/core/src/io/anuke/mindustry/io/SaveIO.java @@ -11,9 +11,7 @@ import java.util.zip.InflaterInputStream; import static io.anuke.mindustry.Vars.*; -//TODO load backup meta if possible public class SaveIO{ - public static final IntArray breakingVersions = IntArray.with(47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 63); public static final IntMap versions = new IntMap<>(); public static final Array versionArray = Array.with(new Save1()); @@ -30,11 +28,11 @@ public class SaveIO{ public static void saveToSlot(int slot){ FileHandle file = fileFor(slot); boolean exists = file.exists(); - if(exists) file.moveTo(file.sibling(file.name() + "-backup." + file.extension())); + if(exists) file.moveTo(backupFileFor(file)); try{ write(fileFor(slot)); }catch(Exception e){ - if(exists) file.sibling(file.name() + "-backup." + file.extension()).moveTo(file); + if(exists) backupFileFor(file).moveTo(file); throw new RuntimeException(e); } } @@ -47,12 +45,12 @@ public class SaveIO{ return new DataInputStream(new InflaterInputStream(fileFor(slot).read(bufferSize))); } + public static DataInputStream getBackupSlotStream(int slot){ + return new DataInputStream(new InflaterInputStream(backupFileFor(fileFor(slot)).read(bufferSize))); + } + public static boolean isSaveValid(int slot){ - try{ - return isSaveValid(getSlotStream(slot)); - }catch(Exception e){ - return false; - } + return isSaveValid(getSlotStream(slot)) || isSaveValid(getBackupSlotStream(slot)); } public static boolean isSaveValid(FileHandle file){ @@ -60,7 +58,6 @@ public class SaveIO{ } public static boolean isSaveValid(DataInputStream stream){ - try{ getData(stream); return true; @@ -90,6 +87,10 @@ public class SaveIO{ return saveDirectory.child(slot + "." + Vars.saveExtension); } + public static FileHandle backupFileFor(FileHandle file){ + return file.sibling(file.name() + "-backup." + file.extension()); + } + public static void write(FileHandle file){ write(new DeflaterOutputStream(file.write(false, bufferSize)){ byte[] tmp = {0}; diff --git a/core/src/io/anuke/mindustry/ui/dialogs/LoadDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/LoadDialog.java index 01e1126217..efdaf0adbd 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/LoadDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/LoadDialog.java @@ -185,12 +185,7 @@ public class LoadDialog extends FloatingDialog{ button.clicked(() -> { if(!button.childrenPressed()){ int build = slot.getBuild(); - if(SaveIO.breakingVersions.contains(build)){ - ui.showInfo("$save.old"); - slot.delete(); - }else{ - runLoadSave(slot); - } + runLoadSave(slot); } }); } diff --git a/core/src/io/anuke/mindustry/world/ItemBuffer.java b/core/src/io/anuke/mindustry/world/ItemBuffer.java index 251517b379..3c80a87f54 100644 --- a/core/src/io/anuke/mindustry/world/ItemBuffer.java +++ b/core/src/io/anuke/mindustry/world/ItemBuffer.java @@ -1,8 +1,11 @@ package io.anuke.mindustry.world; -import io.anuke.arc.util.*; +import io.anuke.arc.util.Pack; +import io.anuke.arc.util.Time; import io.anuke.mindustry.type.Item; +import java.io.*; + import static io.anuke.mindustry.Vars.content; public class ItemBuffer{ @@ -57,4 +60,23 @@ public class ItemBuffer{ System.arraycopy(buffer, 1, buffer, 0, index - 1); index--; } + + public void write(DataOutput stream) throws IOException{ + stream.writeByte((byte)index); + stream.writeByte((byte)buffer.length); + for(long l : buffer){ + stream.writeLong(l); + } + } + + public void read(DataInput stream) throws IOException{ + index = stream.readByte(); + byte length = stream.readByte(); + for(int i = 0; i < length; i++){ + long l = stream.readLong(); + if(i < buffer.length){ + buffer[i] = l; + } + } + } } diff --git a/core/src/io/anuke/mindustry/world/Tile.java b/core/src/io/anuke/mindustry/world/Tile.java index 8b418769dd..0427d6da09 100644 --- a/core/src/io/anuke/mindustry/world/Tile.java +++ b/core/src/io/anuke/mindustry/world/Tile.java @@ -24,9 +24,11 @@ public class Tile implements Position, TargetTrait{ public short x, y; protected Block block; protected Floor floor; - /** Rotation, 0-3. Also used to store offload location, in which case it can be any number. */ + /** Rotation, 0-3. Also used to store offload location and link, in which case it can be any number. + * When saved in non-link form, this data is truncated to 4 bits = max 16.*/ private byte rotation; - /** Team ordinal. */ + /** Team ordinal. Keep in mind that this is written as 4 bits, which means that there are only 2^4 = 16 possible teams. + * Complications may arise from using signed bytes as well. Be careful.*/ private byte team; /** Ore that is on top of this (floor) block. */ private byte overlay = 0; diff --git a/core/src/io/anuke/mindustry/world/blocks/distribution/BufferedItemBridge.java b/core/src/io/anuke/mindustry/world/blocks/distribution/BufferedItemBridge.java index 709c84300e..8c692f9b5d 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/BufferedItemBridge.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/BufferedItemBridge.java @@ -6,6 +6,8 @@ import io.anuke.mindustry.type.Item; import io.anuke.mindustry.world.ItemBuffer; import io.anuke.mindustry.world.Tile; +import java.io.*; + public class BufferedItemBridge extends ExtendingItemBridge{ protected int timerAccept = timers++; @@ -43,5 +45,17 @@ public class BufferedItemBridge extends ExtendingItemBridge{ class BufferedItemBridgeEntity extends ItemBridgeEntity{ ItemBuffer buffer = new ItemBuffer(bufferCapacity, speed); + + @Override + public void write(DataOutput stream) throws IOException{ + super.write(stream); + buffer.write(stream); + } + + @Override + public void read(DataInput stream) throws IOException{ + super.read(stream); + buffer.read(stream); + } } } diff --git a/core/src/io/anuke/mindustry/world/blocks/distribution/Junction.java b/core/src/io/anuke/mindustry/world/blocks/distribution/Junction.java index b3f0227266..f2c652e0f9 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/Junction.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/Junction.java @@ -7,6 +7,8 @@ import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.meta.BlockGroup; +import java.io.*; + import static io.anuke.mindustry.Vars.content; public class Junction extends Block{ @@ -82,6 +84,20 @@ public class Junction extends Block{ class JunctionEntity extends TileEntity{ Buffer[] buffers = {new Buffer(), new Buffer(), new Buffer(), new Buffer()}; + + @Override + public void write(DataOutput stream) throws IOException{ + for(Buffer b : buffers){ + b.write(stream); + } + } + + @Override + public void read(DataInput stream) throws IOException{ + for(Buffer b : buffers){ + b.read(stream); + } + } } class Buffer{ @@ -96,5 +112,24 @@ public class Junction extends Block{ boolean full(){ return index >= items.length - 1; } + + void write(DataOutput stream) throws IOException{ + stream.writeByte((byte)index); + stream.writeByte((byte)items.length); + for(long l : items){ + stream.writeLong(l); + } + } + + void read(DataInput stream) throws IOException{ + index = stream.readByte(); + byte length = stream.readByte(); + for(int i = 0; i < length; i++){ + long l = stream.readLong(); + if(i < items.length){ + items[i] = l; + } + } + } } } diff --git a/core/src/io/anuke/mindustry/world/blocks/distribution/Sorter.java b/core/src/io/anuke/mindustry/world/blocks/distribution/Sorter.java index e62e0da1a8..2f17b814ec 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/Sorter.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/Sorter.java @@ -132,12 +132,12 @@ public class Sorter extends Block{ public Item sortItem; @Override - public void writeConfig(DataOutput stream) throws IOException{ + public void write(DataOutput stream) throws IOException{ stream.writeByte(sortItem == null ? -1 : sortItem.id); } @Override - public void readConfig(DataInput stream) throws IOException{ + public void read(DataInput stream) throws IOException{ byte b = stream.readByte(); sortItem = b == -1 ? null : content.items().get(b); } diff --git a/core/src/io/anuke/mindustry/world/blocks/sandbox/LiquidSource.java b/core/src/io/anuke/mindustry/world/blocks/sandbox/LiquidSource.java index 35723aa00d..970e64245e 100644 --- a/core/src/io/anuke/mindustry/world/blocks/sandbox/LiquidSource.java +++ b/core/src/io/anuke/mindustry/world/blocks/sandbox/LiquidSource.java @@ -116,12 +116,12 @@ public class LiquidSource extends Block{ public Liquid source = null; @Override - public void writeConfig(DataOutput stream) throws IOException{ + public void write(DataOutput stream) throws IOException{ stream.writeByte(source == null ? -1 : source.id); } @Override - public void readConfig(DataInput stream) throws IOException{ + public void read(DataInput stream) throws IOException{ byte id = stream.readByte(); source = id == -1 ? null : content.liquid(id); } diff --git a/core/src/io/anuke/mindustry/world/blocks/storage/Unloader.java b/core/src/io/anuke/mindustry/world/blocks/storage/Unloader.java index f755175992..ba01ffdc2a 100644 --- a/core/src/io/anuke/mindustry/world/blocks/storage/Unloader.java +++ b/core/src/io/anuke/mindustry/world/blocks/storage/Unloader.java @@ -104,12 +104,12 @@ public class Unloader extends Block{ public Item sortItem = null; @Override - public void writeConfig(DataOutput stream) throws IOException{ + public void write(DataOutput stream) throws IOException{ stream.writeByte(sortItem == null ? -1 : sortItem.id); } @Override - public void readConfig(DataInput stream) throws IOException{ + public void read(DataInput stream) throws IOException{ byte id = stream.readByte(); sortItem = id == -1 ? null : content.items().get(id); } diff --git a/core/src/io/anuke/mindustry/world/blocks/units/UnitFactory.java b/core/src/io/anuke/mindustry/world/blocks/units/UnitFactory.java index 6a17d50abb..a90cb448b7 100644 --- a/core/src/io/anuke/mindustry/world/blocks/units/UnitFactory.java +++ b/core/src/io/anuke/mindustry/world/blocks/units/UnitFactory.java @@ -197,14 +197,15 @@ public class UnitFactory extends Block{ @Override public void write(DataOutput stream) throws IOException{ + super.write(stream); stream.writeFloat(buildTime); stream.writeInt(spawned); } @Override public void read(DataInput stream) throws IOException{ + super.read(stream); buildTime = stream.readFloat(); - stream.readFloat(); //unneeded information, will remove later spawned = stream.readInt(); } }