WIP save refactoring
This commit is contained in:
@@ -4,12 +4,27 @@ import java.lang.annotation.*;
|
|||||||
|
|
||||||
public class Annotations{
|
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})
|
@Target({ElementType.METHOD, ElementType.FIELD})
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface Nullable{
|
public @interface Nullable{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Indicates that a method return or field cannot be null.*/
|
||||||
@Target({ElementType.METHOD, ElementType.FIELD})
|
@Target({ElementType.METHOD, ElementType.FIELD})
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface NonNull{
|
public @interface NonNull{
|
||||||
|
|||||||
@@ -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<? extends TypeElement> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<Object, Trees> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
io.anuke.annotations.RemoteMethodAnnotationProcessor
|
io.anuke.annotations.RemoteMethodAnnotationProcessor
|
||||||
io.anuke.annotations.SerializeAnnotationProcessor
|
io.anuke.annotations.SerializeAnnotationProcessor
|
||||||
io.anuke.annotations.StructAnnotationProcessor
|
io.anuke.annotations.StructAnnotationProcessor
|
||||||
|
io.anuke.annotations.CallSuperAnnotationProcessor
|
||||||
26
build.gradle
26
build.gradle
@@ -177,6 +177,31 @@ project(":core"){
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies{
|
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("arc-core")
|
||||||
compile arcModule("extensions:freetype")
|
compile arcModule("extensions:freetype")
|
||||||
compile arcModule("extensions:arcnet")
|
compile arcModule("extensions:arcnet")
|
||||||
@@ -227,6 +252,7 @@ project(":annotations"){
|
|||||||
|
|
||||||
dependencies{
|
dependencies{
|
||||||
compile 'com.squareup:javapoet:1.11.0'
|
compile 'com.squareup:javapoet:1.11.0'
|
||||||
|
compile files("${System.getProperty('java.home')}/../lib/tools.jar")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import io.anuke.arc.collection.IntSet;
|
|||||||
import io.anuke.arc.graphics.Color;
|
import io.anuke.arc.graphics.Color;
|
||||||
import io.anuke.arc.math.RandomXS128;
|
import io.anuke.arc.math.RandomXS128;
|
||||||
import io.anuke.arc.util.*;
|
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.arc.util.serialization.Base64Coder;
|
||||||
import io.anuke.mindustry.Vars;
|
import io.anuke.mindustry.Vars;
|
||||||
import io.anuke.mindustry.core.GameState.State;
|
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. */
|
/** List of entities that were removed, and need not be added while syncing. */
|
||||||
private IntSet removed = new IntSet();
|
private IntSet removed = new IntSet();
|
||||||
/** Byte stream for reading in snapshots. */
|
/** Byte stream for reading in snapshots. */
|
||||||
private ReusableByteArrayInputStream byteStream = new ReusableByteArrayInputStream();
|
private ReusableByteInStream byteStream = new ReusableByteInStream();
|
||||||
private DataInputStream dataStream = new DataInputStream(byteStream);
|
private DataInputStream dataStream = new DataInputStream(byteStream);
|
||||||
|
|
||||||
public NetClient(){
|
public NetClient(){
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import io.anuke.arc.math.geom.Rectangle;
|
|||||||
import io.anuke.arc.math.geom.Vector2;
|
import io.anuke.arc.math.geom.Vector2;
|
||||||
import io.anuke.arc.util.*;
|
import io.anuke.arc.util.*;
|
||||||
import io.anuke.arc.util.io.ByteBufferOutput;
|
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.content.Blocks;
|
||||||
import io.anuke.mindustry.core.GameState.State;
|
import io.anuke.mindustry.core.GameState.State;
|
||||||
import io.anuke.mindustry.entities.Entities;
|
import io.anuke.mindustry.entities.Entities;
|
||||||
@@ -56,7 +56,7 @@ public class NetServer implements ApplicationListener{
|
|||||||
private ByteBufferOutput outputBuffer = new ByteBufferOutput(writeBuffer);
|
private ByteBufferOutput outputBuffer = new ByteBufferOutput(writeBuffer);
|
||||||
|
|
||||||
/** Stream for writing player sync data to. */
|
/** 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. */
|
/** Data stream for writing player sync data to. */
|
||||||
private DataOutputStream dataStream = new DataOutputStream(syncStream);
|
private DataOutputStream dataStream = new DataOutputStream(syncStream);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package io.anuke.mindustry.entities.type;
|
package io.anuke.mindustry.entities.type;
|
||||||
|
|
||||||
import io.anuke.annotations.Annotations.Loc;
|
import io.anuke.annotations.Annotations.*;
|
||||||
import io.anuke.annotations.Annotations.Remote;
|
|
||||||
import io.anuke.arc.Events;
|
import io.anuke.arc.Events;
|
||||||
import io.anuke.arc.collection.Array;
|
import io.anuke.arc.collection.Array;
|
||||||
import io.anuke.arc.collection.ObjectSet;
|
import io.anuke.arc.collection.ObjectSet;
|
||||||
@@ -115,18 +114,14 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
|
|||||||
return dead || tile.entity != this;
|
return dead || tile.entity != this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
public void write(DataOutput stream) throws IOException{
|
public void write(DataOutput stream) throws IOException{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeConfig(DataOutput stream) throws IOException{
|
@CallSuper
|
||||||
}
|
|
||||||
|
|
||||||
public void read(DataInput stream) throws IOException{
|
public void read(DataInput stream) throws IOException{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readConfig(DataInput stream) throws IOException{
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean collide(Bullet other){
|
public boolean collide(Bullet other){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,7 +171,9 @@ public class MapIO{
|
|||||||
}else if(tile.entity != null){
|
}else if(tile.entity != null){
|
||||||
stream.writeByte(Pack.byteByte(tile.getTeamID(), tile.getRotation())); //team + rotation
|
stream.writeByte(Pack.byteByte(tile.getTeamID(), tile.getRotation())); //team + rotation
|
||||||
stream.writeShort(/*(short)tile.entity.health*/tile.block().health); //health
|
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{
|
}else{
|
||||||
//write consecutive non-entity blocks
|
//write consecutive non-entity blocks
|
||||||
int consecutives = 0;
|
int consecutives = 0;
|
||||||
@@ -305,7 +307,9 @@ public class MapIO{
|
|||||||
tile.entity.health = /*health*/tile.block().health;
|
tile.entity.health = /*health*/tile.block().health;
|
||||||
tile.setRotation(rotation);
|
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
|
}else{ //no entity/part, read consecutives
|
||||||
int consecutives = stream.readUnsignedByte();
|
int consecutives = stream.readUnsignedByte();
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package io.anuke.mindustry.io;
|
package io.anuke.mindustry.io;
|
||||||
|
|
||||||
import io.anuke.arc.collection.Array;
|
import io.anuke.arc.collection.*;
|
||||||
import io.anuke.arc.collection.ObjectMap;
|
import io.anuke.arc.collection.ObjectMap.Entry;
|
||||||
import io.anuke.arc.util.Pack;
|
import io.anuke.arc.util.Pack;
|
||||||
|
import io.anuke.arc.util.io.ReusableByteOutStream;
|
||||||
import io.anuke.mindustry.content.Blocks;
|
import io.anuke.mindustry.content.Blocks;
|
||||||
import io.anuke.mindustry.entities.Entities;
|
import io.anuke.mindustry.entities.Entities;
|
||||||
import io.anuke.mindustry.entities.EntityGroup;
|
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.content;
|
||||||
import static io.anuke.mindustry.Vars.world;
|
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 abstract class SaveFileVersion{
|
||||||
public final int version;
|
public final int version;
|
||||||
|
|
||||||
|
private final ReusableByteOutStream byteOutput = new ReusableByteOutStream();
|
||||||
|
private final DataOutputStream dataBytes = new DataOutputStream(byteOutput);
|
||||||
private final ObjectMap<String, String> fallback = ObjectMap.of(
|
private final ObjectMap<String, String> fallback = ObjectMap.of(
|
||||||
"alpha-dart-mech-pad", "dart-mech-pad"
|
"alpha-dart-mech-pad", "dart-mech-pad"
|
||||||
);
|
);
|
||||||
@@ -28,6 +42,42 @@ public abstract class SaveFileVersion{
|
|||||||
this.version = version;
|
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<DataOutput> 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<DataInput> 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{
|
public SaveMeta getData(DataInputStream stream) throws IOException{
|
||||||
long time = stream.readLong();
|
long time = stream.readLong();
|
||||||
long playtime = stream.readLong();
|
long playtime = stream.readLong();
|
||||||
@@ -39,6 +89,23 @@ public abstract class SaveFileVersion{
|
|||||||
return new SaveMeta(version, time, playtime, build, map, wave, rules);
|
return new SaveMeta(version, time, playtime, build, map, wave, rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void writeMeta(DataOutputStream stream, ObjectMap<String, String> map) throws IOException{
|
||||||
|
stream.writeShort(map.size);
|
||||||
|
for(Entry<String, String> 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{
|
public void writeMap(DataOutputStream stream) throws IOException{
|
||||||
//write world size
|
//write world size
|
||||||
stream.writeShort(world.width());
|
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.liquids != null) tile.entity.liquids.write(stream);
|
||||||
if(tile.entity.cons != null) tile.entity.cons.write(stream);
|
if(tile.entity.cons != null) tile.entity.cons.write(stream);
|
||||||
|
|
||||||
tile.entity.writeConfig(stream);
|
|
||||||
tile.entity.write(stream);
|
tile.entity.write(stream);
|
||||||
}else{
|
}else{
|
||||||
//write consecutive non-entity blocks
|
//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.liquids != null) tile.entity.liquids.read(stream);
|
||||||
if(tile.entity.cons != null) tile.entity.cons.read(stream);
|
if(tile.entity.cons != null) tile.entity.cons.read(stream);
|
||||||
|
|
||||||
tile.entity.readConfig(stream);
|
|
||||||
tile.entity.read(stream);
|
tile.entity.read(stream);
|
||||||
}else{
|
}else{
|
||||||
int consecutives = stream.readUnsignedByte();
|
int consecutives = stream.readUnsignedByte();
|
||||||
@@ -255,4 +320,8 @@ public abstract class SaveFileVersion{
|
|||||||
public abstract void read(DataInputStream stream) throws IOException;
|
public abstract void read(DataInputStream stream) throws IOException;
|
||||||
|
|
||||||
public abstract void write(DataOutputStream stream) throws IOException;
|
public abstract void write(DataOutputStream stream) throws IOException;
|
||||||
|
|
||||||
|
public interface IORunner<T>{
|
||||||
|
void accept(T stream) throws IOException;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ import java.util.zip.InflaterInputStream;
|
|||||||
|
|
||||||
import static io.anuke.mindustry.Vars.*;
|
import static io.anuke.mindustry.Vars.*;
|
||||||
|
|
||||||
//TODO load backup meta if possible
|
|
||||||
public class SaveIO{
|
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<SaveFileVersion> versions = new IntMap<>();
|
public static final IntMap<SaveFileVersion> versions = new IntMap<>();
|
||||||
public static final Array<SaveFileVersion> versionArray = Array.with(new Save1());
|
public static final Array<SaveFileVersion> versionArray = Array.with(new Save1());
|
||||||
|
|
||||||
@@ -30,11 +28,11 @@ public class SaveIO{
|
|||||||
public static void saveToSlot(int slot){
|
public static void saveToSlot(int slot){
|
||||||
FileHandle file = fileFor(slot);
|
FileHandle file = fileFor(slot);
|
||||||
boolean exists = file.exists();
|
boolean exists = file.exists();
|
||||||
if(exists) file.moveTo(file.sibling(file.name() + "-backup." + file.extension()));
|
if(exists) file.moveTo(backupFileFor(file));
|
||||||
try{
|
try{
|
||||||
write(fileFor(slot));
|
write(fileFor(slot));
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
if(exists) file.sibling(file.name() + "-backup." + file.extension()).moveTo(file);
|
if(exists) backupFileFor(file).moveTo(file);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,12 +45,12 @@ public class SaveIO{
|
|||||||
return new DataInputStream(new InflaterInputStream(fileFor(slot).read(bufferSize)));
|
return new DataInputStream(new InflaterInputStream(fileFor(slot).read(bufferSize)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isSaveValid(int slot){
|
public static DataInputStream getBackupSlotStream(int slot){
|
||||||
try{
|
return new DataInputStream(new InflaterInputStream(backupFileFor(fileFor(slot)).read(bufferSize)));
|
||||||
return isSaveValid(getSlotStream(slot));
|
|
||||||
}catch(Exception e){
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isSaveValid(int slot){
|
||||||
|
return isSaveValid(getSlotStream(slot)) || isSaveValid(getBackupSlotStream(slot));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isSaveValid(FileHandle file){
|
public static boolean isSaveValid(FileHandle file){
|
||||||
@@ -60,7 +58,6 @@ public class SaveIO{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isSaveValid(DataInputStream stream){
|
public static boolean isSaveValid(DataInputStream stream){
|
||||||
|
|
||||||
try{
|
try{
|
||||||
getData(stream);
|
getData(stream);
|
||||||
return true;
|
return true;
|
||||||
@@ -90,6 +87,10 @@ public class SaveIO{
|
|||||||
return saveDirectory.child(slot + "." + Vars.saveExtension);
|
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){
|
public static void write(FileHandle file){
|
||||||
write(new DeflaterOutputStream(file.write(false, bufferSize)){
|
write(new DeflaterOutputStream(file.write(false, bufferSize)){
|
||||||
byte[] tmp = {0};
|
byte[] tmp = {0};
|
||||||
|
|||||||
@@ -185,13 +185,8 @@ public class LoadDialog extends FloatingDialog{
|
|||||||
button.clicked(() -> {
|
button.clicked(() -> {
|
||||||
if(!button.childrenPressed()){
|
if(!button.childrenPressed()){
|
||||||
int build = slot.getBuild();
|
int build = slot.getBuild();
|
||||||
if(SaveIO.breakingVersions.contains(build)){
|
|
||||||
ui.showInfo("$save.old");
|
|
||||||
slot.delete();
|
|
||||||
}else{
|
|
||||||
runLoadSave(slot);
|
runLoadSave(slot);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
package io.anuke.mindustry.world;
|
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 io.anuke.mindustry.type.Item;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.content;
|
import static io.anuke.mindustry.Vars.content;
|
||||||
|
|
||||||
public class ItemBuffer{
|
public class ItemBuffer{
|
||||||
@@ -57,4 +60,23 @@ public class ItemBuffer{
|
|||||||
System.arraycopy(buffer, 1, buffer, 0, index - 1);
|
System.arraycopy(buffer, 1, buffer, 0, index - 1);
|
||||||
index--;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,9 +24,11 @@ public class Tile implements Position, TargetTrait{
|
|||||||
public short x, y;
|
public short x, y;
|
||||||
protected Block block;
|
protected Block block;
|
||||||
protected Floor floor;
|
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;
|
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;
|
private byte team;
|
||||||
/** Ore that is on top of this (floor) block. */
|
/** Ore that is on top of this (floor) block. */
|
||||||
private byte overlay = 0;
|
private byte overlay = 0;
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import io.anuke.mindustry.type.Item;
|
|||||||
import io.anuke.mindustry.world.ItemBuffer;
|
import io.anuke.mindustry.world.ItemBuffer;
|
||||||
import io.anuke.mindustry.world.Tile;
|
import io.anuke.mindustry.world.Tile;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
public class BufferedItemBridge extends ExtendingItemBridge{
|
public class BufferedItemBridge extends ExtendingItemBridge{
|
||||||
protected int timerAccept = timers++;
|
protected int timerAccept = timers++;
|
||||||
|
|
||||||
@@ -43,5 +45,17 @@ public class BufferedItemBridge extends ExtendingItemBridge{
|
|||||||
|
|
||||||
class BufferedItemBridgeEntity extends ItemBridgeEntity{
|
class BufferedItemBridgeEntity extends ItemBridgeEntity{
|
||||||
ItemBuffer buffer = new ItemBuffer(bufferCapacity, speed);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import io.anuke.mindustry.world.Block;
|
|||||||
import io.anuke.mindustry.world.Tile;
|
import io.anuke.mindustry.world.Tile;
|
||||||
import io.anuke.mindustry.world.meta.BlockGroup;
|
import io.anuke.mindustry.world.meta.BlockGroup;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.content;
|
import static io.anuke.mindustry.Vars.content;
|
||||||
|
|
||||||
public class Junction extends Block{
|
public class Junction extends Block{
|
||||||
@@ -82,6 +84,20 @@ public class Junction extends Block{
|
|||||||
|
|
||||||
class JunctionEntity extends TileEntity{
|
class JunctionEntity extends TileEntity{
|
||||||
Buffer[] buffers = {new Buffer(), new Buffer(), new Buffer(), new Buffer()};
|
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{
|
class Buffer{
|
||||||
@@ -96,5 +112,24 @@ public class Junction extends Block{
|
|||||||
boolean full(){
|
boolean full(){
|
||||||
return index >= items.length - 1;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,12 +132,12 @@ public class Sorter extends Block{
|
|||||||
public Item sortItem;
|
public Item sortItem;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeConfig(DataOutput stream) throws IOException{
|
public void write(DataOutput stream) throws IOException{
|
||||||
stream.writeByte(sortItem == null ? -1 : sortItem.id);
|
stream.writeByte(sortItem == null ? -1 : sortItem.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readConfig(DataInput stream) throws IOException{
|
public void read(DataInput stream) throws IOException{
|
||||||
byte b = stream.readByte();
|
byte b = stream.readByte();
|
||||||
sortItem = b == -1 ? null : content.items().get(b);
|
sortItem = b == -1 ? null : content.items().get(b);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,12 +116,12 @@ public class LiquidSource extends Block{
|
|||||||
public Liquid source = null;
|
public Liquid source = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeConfig(DataOutput stream) throws IOException{
|
public void write(DataOutput stream) throws IOException{
|
||||||
stream.writeByte(source == null ? -1 : source.id);
|
stream.writeByte(source == null ? -1 : source.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readConfig(DataInput stream) throws IOException{
|
public void read(DataInput stream) throws IOException{
|
||||||
byte id = stream.readByte();
|
byte id = stream.readByte();
|
||||||
source = id == -1 ? null : content.liquid(id);
|
source = id == -1 ? null : content.liquid(id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,12 +104,12 @@ public class Unloader extends Block{
|
|||||||
public Item sortItem = null;
|
public Item sortItem = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeConfig(DataOutput stream) throws IOException{
|
public void write(DataOutput stream) throws IOException{
|
||||||
stream.writeByte(sortItem == null ? -1 : sortItem.id);
|
stream.writeByte(sortItem == null ? -1 : sortItem.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readConfig(DataInput stream) throws IOException{
|
public void read(DataInput stream) throws IOException{
|
||||||
byte id = stream.readByte();
|
byte id = stream.readByte();
|
||||||
sortItem = id == -1 ? null : content.items().get(id);
|
sortItem = id == -1 ? null : content.items().get(id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,14 +197,15 @@ public class UnitFactory extends Block{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(DataOutput stream) throws IOException{
|
public void write(DataOutput stream) throws IOException{
|
||||||
|
super.write(stream);
|
||||||
stream.writeFloat(buildTime);
|
stream.writeFloat(buildTime);
|
||||||
stream.writeInt(spawned);
|
stream.writeInt(spawned);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read(DataInput stream) throws IOException{
|
public void read(DataInput stream) throws IOException{
|
||||||
|
super.read(stream);
|
||||||
buildTime = stream.readFloat();
|
buildTime = stream.readFloat();
|
||||||
stream.readFloat(); //unneeded information, will remove later
|
|
||||||
spawned = stream.readInt();
|
spawned = stream.readInt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user