diff --git a/README.md b/README.md index 2f0cab8ff3..771f4a3107 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,15 @@ First, make sure you have [JDK 8](https://adoptopenjdk.net/) installed. Open a t #### Windows -_Running:_ `gradlew desktop:run` -_Building:_ `gradlew desktop:dist` +_Running:_ `gradlew desktop:run` +_Building:_ `gradlew desktop:dist` +_Sprite Packing:_ `gradlew tools:pack` #### Linux/Mac OS -_Running:_ `./gradlew desktop:run` -_Building:_ `./gradlew desktop:dist` +_Running:_ `./gradlew desktop:run` +_Building:_ `./gradlew desktop:dist` +_Sprite Packing:_ `./gradlew tools:pack` #### Server diff --git a/android/src/mindustry/android/AndroidLauncher.java b/android/src/mindustry/android/AndroidLauncher.java index 561ce0b998..6faa60a9ee 100644 --- a/android/src/mindustry/android/AndroidLauncher.java +++ b/android/src/mindustry/android/AndroidLauncher.java @@ -177,6 +177,7 @@ public class AndroidLauncher extends AndroidApplication{ } //create marker Core.files.local("files_moved").writeString("files moved to " + data); + Core.files.local("files_moved_103").writeString("files moved again"); Log.info("Files moved."); }catch(Throwable t){ Log.err("Failed to move files!"); diff --git a/annotations/src/main/java/mindustry/annotations/Annotations.java b/annotations/src/main/java/mindustry/annotations/Annotations.java index 8124930825..6441cacf2f 100644 --- a/annotations/src/main/java/mindustry/annotations/Annotations.java +++ b/annotations/src/main/java/mindustry/annotations/Annotations.java @@ -3,10 +3,40 @@ package mindustry.annotations; import java.lang.annotation.*; public class Annotations{ + //region entity interfaces + + /** Indicates multiple inheritance on a component type. */ + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.SOURCE) + public @interface Depends{ + Class[] value(); + } + + /** Indicates that a component def is present on all entities. */ + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.SOURCE) + public @interface BaseComponent{ + } + + /** Indicates an entity definition. */ + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.SOURCE) + public @interface EntityDef{ + Class[] value(); + } + + /** Indicates an internal interface for entity components. */ + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.SOURCE) + public @interface EntityInterface{ + } + + //endregion + //region misc. utility @Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) - public @interface StyleDefaults { + public @interface StyleDefaults{ } /** Indicates that a method should always call its super version. */ @@ -16,10 +46,10 @@ public class Annotations{ } - /** Annotation that allows overriding CallSuper annotation. To be used on method that overrides method with CallSuper annotation from parent class.*/ + /** 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 { + public @interface OverrideCallSuper{ } /** Marks a class as serializable. */ @@ -29,6 +59,9 @@ public class Annotations{ } + //endregion + //region struct + /** Marks a class as a special value type struct. Class name must end in 'Struct'. */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) @@ -44,6 +77,9 @@ public class Annotations{ int value(); } + //endregion + //region remote + public enum PacketPriority{ /** Gets put in a queue and processed if not connected. */ normal, @@ -138,4 +174,6 @@ public class Annotations{ public @interface ReadClass{ Class value(); } + + //endregion } diff --git a/annotations/src/main/java/mindustry/annotations/BaseProcessor.java b/annotations/src/main/java/mindustry/annotations/BaseProcessor.java index effbb72400..57d3be2d9c 100644 --- a/annotations/src/main/java/mindustry/annotations/BaseProcessor.java +++ b/annotations/src/main/java/mindustry/annotations/BaseProcessor.java @@ -1,9 +1,19 @@ package mindustry.annotations; +import arc.struct.*; +import arc.util.*; +import com.squareup.javapoet.*; +import com.sun.source.util.*; +import mindustry.annotations.util.*; + import javax.annotation.processing.*; import javax.lang.model.*; import javax.lang.model.element.*; import javax.lang.model.util.*; +import javax.tools.Diagnostic.*; +import javax.tools.*; +import java.io.*; +import java.lang.annotation.*; import java.util.*; @SupportedSourceVersion(SourceVersion.RELEASE_8) @@ -15,8 +25,11 @@ public abstract class BaseProcessor extends AbstractProcessor{ public static Elements elementu; public static Filer filer; public static Messager messager; + public static Trees trees; protected int round; + protected int rounds = 1; + protected RoundEnvironment env; public static String getMethodName(Element element){ return ((TypeElement)element.getEnclosingElement()).getQualifiedName().toString() + "." + element.getSimpleName(); @@ -27,19 +40,80 @@ public abstract class BaseProcessor extends AbstractProcessor{ || type.equals("long") || type.equals("float") || type.equals("double") || type.equals("char"); } - @Override - public synchronized void init(ProcessingEnvironment processingEnv){ - super.init(processingEnv); + public static void write(TypeSpec.Builder builder) throws Exception{ + write(builder, null); + } - typeu = processingEnv.getTypeUtils(); - elementu = processingEnv.getElementUtils(); - filer = processingEnv.getFiler(); - messager = processingEnv.getMessager(); + public static void write(TypeSpec.Builder builder, Array imports) throws Exception{ + JavaFile file = JavaFile.builder(packageName, builder.build()).skipJavaLangImports(true).build(); + + if(imports != null){ + String rawSource = file.toString(); + Array result = new Array<>(); + for (String s : rawSource.split("\n", -1)) { + result.add(s); + if (s.startsWith("package ")) { + result.add(""); + for (String i : imports) { + result.add(i); + } + } + } + + String out = result.toString("\n"); + JavaFileObject object = filer.createSourceFile(file.packageName + "." + file.typeSpec.name, file.typeSpec.originatingElements.toArray(new Element[0])); + OutputStream stream = object.openOutputStream(); + stream.write(out.getBytes()); + stream.close(); + }else{ + file.writeTo(filer); + } + } + + public Array types(Class type){ + return Array.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof TypeElement) + .map(e -> new Stype((TypeElement)e)); + } + + public Array fields(Class type){ + return Array.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof VariableElement) + .map(e -> new Svar((VariableElement)e)); + } + + public Array methods(Class type){ + return Array.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof ExecutableElement) + .map(e -> new Smethod((ExecutableElement)e)); + } + + public void err(String message){ + messager.printMessage(Kind.ERROR, message); + Log.err("[CODEGEN ERROR] " +message); + } + + public void err(String message, Element elem){ + messager.printMessage(Kind.ERROR, message, elem); + Log.err("[CODEGEN ERROR] " + message + ": " + elem); + } + + public void err(String message, Selement elem){ + err(message, elem.e); + } + + @Override + public synchronized void init(ProcessingEnvironment env){ + super.init(env); + + trees = Trees.instance(env); + typeu = env.getTypeUtils(); + elementu = env.getElementUtils(); + filer = env.getFiler(); + messager = env.getMessager(); } @Override public boolean process(Set annotations, RoundEnvironment roundEnv){ - if(round++ != 0) return false; //only process 1 round + if(round++ >= rounds) return false; //only process 1 round + this.env = roundEnv; try{ process(roundEnv); }catch(Exception e){ diff --git a/annotations/src/main/java/mindustry/annotations/impl/AssetsAnnotationProcessor.java b/annotations/src/main/java/mindustry/annotations/impl/AssetsProcess.java similarity index 99% rename from annotations/src/main/java/mindustry/annotations/impl/AssetsAnnotationProcessor.java rename to annotations/src/main/java/mindustry/annotations/impl/AssetsProcess.java index 2ac95b7b40..10e258959c 100644 --- a/annotations/src/main/java/mindustry/annotations/impl/AssetsAnnotationProcessor.java +++ b/annotations/src/main/java/mindustry/annotations/impl/AssetsProcess.java @@ -16,7 +16,7 @@ import javax.tools.*; import java.util.*; @SupportedAnnotationTypes("mindustry.annotations.Annotations.StyleDefaults") -public class AssetsAnnotationProcessor extends BaseProcessor{ +public class AssetsProcess extends BaseProcessor{ private String path; @Override diff --git a/annotations/src/main/java/mindustry/annotations/impl/CallSuperAnnotationProcessor.java b/annotations/src/main/java/mindustry/annotations/impl/CallSuperProcess.java similarity index 98% rename from annotations/src/main/java/mindustry/annotations/impl/CallSuperAnnotationProcessor.java rename to annotations/src/main/java/mindustry/annotations/impl/CallSuperProcess.java index 4815073331..b43a2d3eef 100644 --- a/annotations/src/main/java/mindustry/annotations/impl/CallSuperAnnotationProcessor.java +++ b/annotations/src/main/java/mindustry/annotations/impl/CallSuperProcess.java @@ -18,7 +18,7 @@ import java.lang.annotation.*; import java.util.*; @SupportedAnnotationTypes({"java.lang.Override"}) -public class CallSuperAnnotationProcessor extends AbstractProcessor{ +public class CallSuperProcess extends AbstractProcessor{ private Trees trees; @Override @@ -122,6 +122,7 @@ public class CallSuperAnnotationProcessor extends AbstractProcessor{ } for(Symbol s : it){ + if(s instanceof MethodSymbol){ MethodSymbol ms = (MethodSymbol)s; diff --git a/annotations/src/main/java/mindustry/annotations/impl/EntityProcess.java b/annotations/src/main/java/mindustry/annotations/impl/EntityProcess.java new file mode 100644 index 0000000000..80e294a0f3 --- /dev/null +++ b/annotations/src/main/java/mindustry/annotations/impl/EntityProcess.java @@ -0,0 +1,297 @@ +package mindustry.annotations.impl; + +import arc.struct.*; +import arc.util.*; +import com.squareup.javapoet.*; +import com.squareup.javapoet.TypeSpec.*; +import com.sun.source.tree.*; +import mindustry.annotations.Annotations.*; +import mindustry.annotations.*; +import mindustry.annotations.util.*; + +import javax.annotation.processing.*; +import javax.lang.model.element.*; +import javax.lang.model.type.*; + +@SupportedAnnotationTypes({ +"mindustry.annotations.Annotations.EntityDef", +"mindustry.annotations.Annotations.EntityInterface", +"mindustry.annotations.Annotations.BaseComponent" +}) +public class EntityProcess extends BaseProcessor{ + Array definitions = new Array<>(); + Array baseComponents; + ObjectMap> componentDependencies = new ObjectMap<>(); + ObjectMap> defComponents = new ObjectMap<>(); + ObjectSet imports = new ObjectSet<>(); + + { + rounds = 2; + } + + @Override + public void process(RoundEnvironment env) throws Exception{ + + //round 1: get component classes and generate interfaces for them + if(round == 1){ + baseComponents = types(BaseComponent.class); + Array allDefs = types(EntityDef.class); + + ObjectSet allComponents = new ObjectSet<>(); + + //find all components used... + for(Stype type : allDefs){ + allComponents.addAll(allComponents(type)); + } + + //add all components w/ dependencies + allComponents.addAll(types(Depends.class).map(s -> Array.withArrays(getDependencies(s), s)).flatten()); + + //add component imports + for(Stype comp : allComponents){ + imports.addAll(getImports(comp.e)); + } + + //create component interfaces + for(Stype component : allComponents){ + TypeSpec.Builder inter = TypeSpec.interfaceBuilder(interfaceName(component)).addModifiers(Modifier.PUBLIC).addAnnotation(EntityInterface.class); + + //implement extra interfaces these components may have, e.g. position + for(Stype extraInterface : component.interfaces()){ + inter.addSuperinterface(extraInterface.mirror()); + } + + //implement super interfaces + Array depends = getDependencies(component); + for(Stype type : depends){ + inter.addSuperinterface(ClassName.get(packageName, interfaceName(type))); + } + + for(Svar field : component.fields().select(e -> !e.is(Modifier.STATIC) && !e.is(Modifier.PRIVATE) && !e.is(Modifier.TRANSIENT))){ + String cname = Strings.capitalize(field.name()); + //getter + inter.addMethod(MethodSpec.methodBuilder("get" + cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC).returns(field.tname()).build()); + //setter + if(!field.is(Modifier.FINAL)) inter.addMethod(MethodSpec.methodBuilder("set" + cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC).addParameter(field.tname(), field.name()).build()); + } + + //add utility methods to interface + for(Smethod method : component.methods()){ + inter.addMethod(MethodSpec.methodBuilder(method.name()) + .addExceptions(method.thrownt()) + .addTypeVariables(method.typeVariables().map(TypeVariableName::get)) + .returns(method.ret().toString().equals("void") ? TypeName.VOID : method.retn()) + .addParameters(method.params().map(v -> ParameterSpec.builder(v.tname(), v.name()) + .build())).addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT).build()); + } + + write(inter); + } + + //look at each definition + for(Stype type : allDefs){ + if(!type.name().endsWith("Def")){ + err("All entity def names must end with 'Def'", type.e); + } + String name = type.name().replace("Def", "Gen"); //TODO remove 'gen' + TypeSpec.Builder builder = TypeSpec.classBuilder(name).addModifiers(Modifier.PUBLIC, Modifier.FINAL); + + Array components = allComponents(type); + ObjectMap> methods = new ObjectMap<>(); + + //add all components + for(Stype comp : components){ + + //write fields to the class; ignoring transient ones + Array fields = comp.fields().select(f -> !f.is(Modifier.TRANSIENT)); + for(Svar f : fields){ + VariableTree tree = f.tree(); + FieldSpec.Builder fbuilder = FieldSpec.builder(f.tname(), f.name()); + //add initializer if it exists + if(tree.getInitializer() != null){ + fbuilder.initializer(tree.getInitializer().toString()); + } + builder.addField(fbuilder.build()); + } + + //get all utility methods from components + for(Smethod elem : comp.methods()){ + methods.getOr(elem.toString(), Array::new).add(elem); + } + } + + //add all methods from components + for(ObjectMap.Entry> entry : methods){ + //representative method + Smethod first = entry.value.first(); + //build method using same params/returns + MethodSpec.Builder mbuilder = MethodSpec.methodBuilder(first.name()).addModifiers(Modifier.PUBLIC, Modifier.FINAL); + mbuilder.addTypeVariables(first.typeVariables().map(TypeVariableName::get)); + mbuilder.returns(first.retn()); + mbuilder.addExceptions(first.thrownt()); + + for(Svar var : first.params()){ + mbuilder.addParameter(var.tname(), var.name()); + } + + //only write the block if it's a void method with several entries + boolean writeBlock = first.ret().toString().equals("void") && entry.value.size > 1; + + if(entry.value.first().is(Modifier.ABSTRACT) && entry.value.size == 1){ + err(entry.value.first().up().getSimpleName() + " declares an abstract method. This method must be implemented in another component", entry.value.first()); + } + + for(Smethod elem : entry.value){ + if(elem.is(Modifier.ABSTRACT)) continue; + + //get all statements in the method, copy them over + MethodTree methodTree = elem.tree(); + BlockTree blockTree = methodTree.getBody(); + String str = blockTree.toString(); + //name for code blocks in the methods + String blockName = elem.up().getSimpleName().toString().toLowerCase().replace("comp", ""); + + //skip empty blocks + if(str.replace("{", "").replace("\n", "").replace("}", "").replace("\t", "").replace(" ", "").isEmpty()){ + continue; + } + + //wrap scope to prevent variable leakage + if(writeBlock){ + //replace return; with block break + str = str.replace("return;", "break " + blockName + ";"); + mbuilder.addCode(blockName + ": {\n"); + } + + //trim block + str = str.substring(2, str.length() - 1); + + //make sure to remove braces here + mbuilder.addCode(str); + + //end scope + if(writeBlock) mbuilder.addCode("}\n"); + } + + builder.addMethod(mbuilder.build()); + } + + definitions.add(new Definition(builder, type)); + + } + }else{ + //round 2: generate actual classes and implement interfaces + Array interfaces = types(EntityInterface.class); + + //implement each definition + for(Definition def : definitions){ + Array components = allComponents(def.base); + + //get interface for each component + for(Stype comp : components){ + //implement the interface + Stype inter = interfaces.find(i -> i.name().equals(interfaceName(comp))); + def.builder.addSuperinterface(inter.tname()); + + //generate getter/setter for each method + for(Smethod method : inter.methods()){ + if(method.name().length() <= 3) continue; + + String var = Strings.camelize(method.name().substring(3)); + //make sure it's a real variable + if(!Array.with(def.builder.fieldSpecs).contains(f -> f.name.equals(var))) continue; + + if(method.name().startsWith("get")){ + def.builder.addMethod(MethodSpec.overriding(method.e).addStatement("return " + var).build()); + }else if(method.name().startsWith("set")){ + def.builder.addMethod(MethodSpec.overriding(method.e).addStatement("this." + var + " = " + var).build()); + } + } + } + + write(def.builder, imports.asArray()); + } + } + } + + Array getImports(Element elem){ + return Array.with(trees.getPath(elem).getCompilationUnit().getImports()).map(Object::toString); + } + + /** @return interface for a component type */ + String interfaceName(Stype comp){ + String suffix = "Comp"; + if(!comp.name().endsWith(suffix)){ + err("All components must have names that end with 'Comp'.", comp.e); + } + return comp.name().substring(0, comp.name().length() - suffix.length()) + "c"; + } + + /** @return all components that a entity def has */ + Array allComponents(Stype type){ + if(!defComponents.containsKey(type)){ + //get base defs + Array components = Array.with(mirrors(type)).map(Stype::of); + ObjectSet out = new ObjectSet<>(); + for(Stype comp : components){ + //get dependencies for each def, add them + out.add(comp); + out.addAll(getDependencies(comp)); + } + + defComponents.put(type, out.asArray()); + } + + return defComponents.get(type); + } + + Array getDependencies(Stype component){ + if(!componentDependencies.containsKey(component)){ + ObjectSet out = new ObjectSet<>(); + out.addAll(component.superclasses()); + + //get dependency classes + if(component.annotation(Depends.class) != null){ + try{ + component.annotation(Depends.class).value(); + }catch(MirroredTypesException e){ + out.addAll(Array.with(e.getTypeMirrors()).map(Stype::of)); + } + } + + //out now contains the base dependencies; finish constructing the tree + ObjectSet result = new ObjectSet<>(); + for(Stype type : out){ + result.add(type); + result.addAll(getDependencies(type)); + } + + if(component.annotation(BaseComponent.class) == null){ + result.addAll(baseComponents); + } + + componentDependencies.put(component, result.asArray()); + } + + return componentDependencies.get(component); + } + + TypeMirror[] mirrors(Stype type){ + try{ + type.annotation(EntityDef.class).value(); + }catch(MirroredTypesException e){ + return e.getTypeMirrors().toArray(new TypeMirror[0]); + } + throw new IllegalArgumentException("Missing components: " + type); + } + + class Definition{ + final TypeSpec.Builder builder; + final Stype base; + + public Definition(Builder builder, Stype base){ + this.builder = builder; + this.base = base; + } + } +} diff --git a/annotations/src/main/java/mindustry/annotations/impl/SerializeAnnotationProcessor.java b/annotations/src/main/java/mindustry/annotations/impl/SerializeProcess.java similarity index 97% rename from annotations/src/main/java/mindustry/annotations/impl/SerializeAnnotationProcessor.java rename to annotations/src/main/java/mindustry/annotations/impl/SerializeProcess.java index 3d07b2abce..9bade20179 100644 --- a/annotations/src/main/java/mindustry/annotations/impl/SerializeAnnotationProcessor.java +++ b/annotations/src/main/java/mindustry/annotations/impl/SerializeProcess.java @@ -15,7 +15,7 @@ import java.util.*; import java.util.zip.*; @SupportedAnnotationTypes("mindustry.annotations.Annotations.Serialize") -public class SerializeAnnotationProcessor extends BaseProcessor{ +public class SerializeProcess extends BaseProcessor{ /** Target class name. */ private static final String className = "Serialization"; /** Name of the base package to put all the generated classes. */ @@ -28,7 +28,7 @@ public class SerializeAnnotationProcessor extends BaseProcessor{ TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC); classBuilder.addStaticBlock(CodeBlock.of(new DataInputStream(new InflaterInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(data)))).readUTF())); classBuilder.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "\"unchecked\"").build()); - classBuilder.addJavadoc(RemoteMethodAnnotationProcessor.autogenWarning); + classBuilder.addJavadoc(RemoteProcess.autogenWarning); MethodSpec.Builder method = MethodSpec.methodBuilder("init").addModifiers(Modifier.PUBLIC, Modifier.STATIC); diff --git a/annotations/src/main/java/mindustry/annotations/impl/StructAnnotationProcessor.java b/annotations/src/main/java/mindustry/annotations/impl/StructProcess.java similarity index 98% rename from annotations/src/main/java/mindustry/annotations/impl/StructAnnotationProcessor.java rename to annotations/src/main/java/mindustry/annotations/impl/StructProcess.java index e4387245c5..b27871d63d 100644 --- a/annotations/src/main/java/mindustry/annotations/impl/StructAnnotationProcessor.java +++ b/annotations/src/main/java/mindustry/annotations/impl/StructProcess.java @@ -20,7 +20,7 @@ import java.util.Set; @SupportedAnnotationTypes({ "mindustry.annotations.Annotations.Struct" }) -public class StructAnnotationProcessor extends BaseProcessor{ +public class StructProcess extends BaseProcessor{ @Override public void process(RoundEnvironment env) throws Exception{ @@ -41,7 +41,7 @@ public class StructAnnotationProcessor extends BaseProcessor{ try{ List variables = ElementFilter.fieldsIn(elem.getEnclosedElements()); - int structSize = variables.stream().mapToInt(StructAnnotationProcessor::varSize).sum(); + int structSize = variables.stream().mapToInt(StructProcess::varSize).sum(); int structTotalSize = (structSize <= 8 ? 8 : structSize <= 16 ? 16 : structSize <= 32 ? 32 : 64); if(variables.size() == 0){ diff --git a/annotations/src/main/java/mindustry/annotations/remote/RemoteMethodAnnotationProcessor.java b/annotations/src/main/java/mindustry/annotations/remote/RemoteMethodAnnotationProcessor.java deleted file mode 100644 index 7f3acc0c86..0000000000 --- a/annotations/src/main/java/mindustry/annotations/remote/RemoteMethodAnnotationProcessor.java +++ /dev/null @@ -1,137 +0,0 @@ -package mindustry.annotations.remote; - -import com.squareup.javapoet.*; -import mindustry.annotations.*; -import mindustry.annotations.Annotations.*; -import mindustry.annotations.remote.IOFinder.*; - -import javax.annotation.processing.*; -import javax.lang.model.element.*; -import javax.tools.Diagnostic.*; -import java.util.*; -import java.util.stream.*; - - -/** The annotation processor for generating remote method call code. */ -@SupportedAnnotationTypes({ -"mindustry.annotations.Annotations.Remote", -"mindustry.annotations.Annotations.WriteClass", -"mindustry.annotations.Annotations.ReadClass", -}) -public class RemoteMethodAnnotationProcessor extends BaseProcessor{ - /** Maximum size of each event packet. */ - public static final int maxPacketSize = 4096; - /** Warning on top of each autogenerated file. */ - public static final String autogenWarning = "Autogenerated file. Do not modify!\n"; - - /** Name of class that handles reading and invoking packets on the server. */ - private static final String readServerName = "RemoteReadServer"; - /** Name of class that handles reading and invoking packets on the client. */ - private static final String readClientName = "RemoteReadClient"; - /** Simple class name of generated class name. */ - private static final String callLocation = "Call"; - - //class serializers - private HashMap serializers; - //all elements with the Remote annotation - private Set elements; - //map of all classes to generate by name - private HashMap classMap; - //list of all method entries - private ArrayList methods; - //list of all method entries - private ArrayList classes; - - @Override - public boolean process(Set annotations, RoundEnvironment roundEnv){ - if(round > 1) return false; //only process 2 rounds - - round++; - - try{ - - //round 1: find all annotations, generate *writers* - if(round == 1){ - //get serializers - serializers = new IOFinder().findSerializers(roundEnv); - //last method ID used - int lastMethodID = 0; - //find all elements with the Remote annotation - elements = roundEnv.getElementsAnnotatedWith(Remote.class); - //map of all classes to generate by name - classMap = new HashMap<>(); - //list of all method entries - methods = new ArrayList<>(); - //list of all method entries - classes = new ArrayList<>(); - - List orderedElements = new ArrayList<>(elements); - orderedElements.sort(Comparator.comparing(Object::toString)); - - //create methods - for(Element element : orderedElements){ - Remote annotation = element.getAnnotation(Remote.class); - - //check for static - if(!element.getModifiers().contains(Modifier.STATIC) || !element.getModifiers().contains(Modifier.PUBLIC)){ - BaseProcessor.messager.printMessage(Kind.ERROR, "All @Remote methods must be public and static: ", element); - } - - //can't generate none methods - if(annotation.targets() == Loc.none){ - BaseProcessor.messager.printMessage(Kind.ERROR, "A @Remote method's targets() cannot be equal to 'none':", element); - } - - //get and create class entry if needed - if(!classMap.containsKey(callLocation)){ - ClassEntry clas = new ClassEntry(callLocation); - classMap.put(callLocation, clas); - classes.add(clas); - } - - ClassEntry entry = classMap.get(callLocation); - - //create and add entry - MethodEntry method = new MethodEntry(entry.name, BaseProcessor.getMethodName(element), annotation.targets(), annotation.variants(), - annotation.called(), annotation.unreliable(), annotation.forward(), lastMethodID++, (ExecutableElement)element, annotation.priority()); - - entry.methods.add(method); - methods.add(method); - } - - //create read/write generators - RemoteWriteGenerator writegen = new RemoteWriteGenerator(serializers); - - //generate the methods to invoke (write) - writegen.generateFor(classes, packageName); - - return true; - }else if(round == 2){ //round 2: generate all *readers* - RemoteReadGenerator readgen = new RemoteReadGenerator(serializers); - - //generate server readers - readgen.generateFor(methods.stream().filter(method -> method.where.isClient).collect(Collectors.toList()), readServerName, packageName, true); - //generate client readers - readgen.generateFor(methods.stream().filter(method -> method.where.isServer).collect(Collectors.toList()), readClientName, packageName, false); - - //create class for storing unique method hash - TypeSpec.Builder hashBuilder = TypeSpec.classBuilder("MethodHash").addModifiers(Modifier.PUBLIC); - hashBuilder.addJavadoc(autogenWarning); - hashBuilder.addField(FieldSpec.builder(int.class, "HASH", Modifier.STATIC, Modifier.PUBLIC, Modifier.FINAL) - .initializer("$1L", Objects.hash(methods)).build()); - - //build and write resulting hash class - TypeSpec spec = hashBuilder.build(); - JavaFile.builder(packageName, spec).build().writeTo(BaseProcessor.filer); - - return true; - } - - }catch(Exception e){ - e.printStackTrace(); - throw new RuntimeException(e); - } - - return false; - } -} diff --git a/annotations/src/main/java/mindustry/annotations/remote/RemoteProcess.java b/annotations/src/main/java/mindustry/annotations/remote/RemoteProcess.java new file mode 100644 index 0000000000..8abd0b82b8 --- /dev/null +++ b/annotations/src/main/java/mindustry/annotations/remote/RemoteProcess.java @@ -0,0 +1,124 @@ +package mindustry.annotations.remote; + +import com.squareup.javapoet.*; +import mindustry.annotations.*; +import mindustry.annotations.Annotations.*; +import mindustry.annotations.remote.IOFinder.*; + +import javax.annotation.processing.*; +import javax.lang.model.element.*; +import javax.tools.Diagnostic.*; +import java.util.*; +import java.util.stream.*; + + +/** The annotation processor for generating remote method call code. */ +@SupportedAnnotationTypes({ +"mindustry.annotations.Annotations.Remote", +"mindustry.annotations.Annotations.WriteClass", +"mindustry.annotations.Annotations.ReadClass", +}) +public class RemoteProcess extends BaseProcessor{ + /** Maximum size of each event packet. */ + public static final int maxPacketSize = 4096; + /** Warning on top of each autogenerated file. */ + public static final String autogenWarning = "Autogenerated file. Do not modify!\n"; + + /** Name of class that handles reading and invoking packets on the server. */ + private static final String readServerName = "RemoteReadServer"; + /** Name of class that handles reading and invoking packets on the client. */ + private static final String readClientName = "RemoteReadClient"; + /** Simple class name of generated class name. */ + private static final String callLocation = "Call"; + + //class serializers + private HashMap serializers; + //all elements with the Remote annotation + private Set elements; + //map of all classes to generate by name + private HashMap classMap; + //list of all method entries + private ArrayList methods; + //list of all method entries + private ArrayList classes; + + { + rounds = 2; + } + + @Override + public void process(RoundEnvironment roundEnv) throws Exception{ + //round 1: find all annotations, generate *writers* + if(round == 1){ + //get serializers + serializers = new IOFinder().findSerializers(roundEnv); + //last method ID used + int lastMethodID = 0; + //find all elements with the Remote annotation + elements = roundEnv.getElementsAnnotatedWith(Remote.class); + //map of all classes to generate by name + classMap = new HashMap<>(); + //list of all method entries + methods = new ArrayList<>(); + //list of all method entries + classes = new ArrayList<>(); + + List orderedElements = new ArrayList<>(elements); + orderedElements.sort(Comparator.comparing(Object::toString)); + + //create methods + for(Element element : orderedElements){ + Remote annotation = element.getAnnotation(Remote.class); + + //check for static + if(!element.getModifiers().contains(Modifier.STATIC) || !element.getModifiers().contains(Modifier.PUBLIC)){ + BaseProcessor.messager.printMessage(Kind.ERROR, "All @Remote methods must be public and static: ", element); + } + + //can't generate none methods + if(annotation.targets() == Loc.none){ + BaseProcessor.messager.printMessage(Kind.ERROR, "A @Remote method's targets() cannot be equal to 'none':", element); + } + + //get and create class entry if needed + if(!classMap.containsKey(callLocation)){ + ClassEntry clas = new ClassEntry(callLocation); + classMap.put(callLocation, clas); + classes.add(clas); + } + + ClassEntry entry = classMap.get(callLocation); + + //create and add entry + MethodEntry method = new MethodEntry(entry.name, BaseProcessor.getMethodName(element), annotation.targets(), annotation.variants(), + annotation.called(), annotation.unreliable(), annotation.forward(), lastMethodID++, (ExecutableElement)element, annotation.priority()); + + entry.methods.add(method); + methods.add(method); + } + + //create read/write generators + RemoteWriteGenerator writegen = new RemoteWriteGenerator(serializers); + + //generate the methods to invoke (write) + writegen.generateFor(classes, packageName); + }else if(round == 2){ //round 2: generate all *readers* + RemoteReadGenerator readgen = new RemoteReadGenerator(serializers); + + //generate server readers + readgen.generateFor(methods.stream().filter(method -> method.where.isClient).collect(Collectors.toList()), readServerName, packageName, true); + //generate client readers + readgen.generateFor(methods.stream().filter(method -> method.where.isServer).collect(Collectors.toList()), readClientName, packageName, false); + + //create class for storing unique method hash + TypeSpec.Builder hashBuilder = TypeSpec.classBuilder("MethodHash").addModifiers(Modifier.PUBLIC); + hashBuilder.addJavadoc(autogenWarning); + hashBuilder.addField(FieldSpec.builder(int.class, "HASH", Modifier.STATIC, Modifier.PUBLIC, Modifier.FINAL) + .initializer("$1L", Objects.hash(methods)).build()); + + //build and write resulting hash class + TypeSpec spec = hashBuilder.build(); + JavaFile.builder(packageName, spec).build().writeTo(BaseProcessor.filer); + } + } +} diff --git a/annotations/src/main/java/mindustry/annotations/remote/RemoteReadGenerator.java b/annotations/src/main/java/mindustry/annotations/remote/RemoteReadGenerator.java index ab7abb2538..211d807067 100644 --- a/annotations/src/main/java/mindustry/annotations/remote/RemoteReadGenerator.java +++ b/annotations/src/main/java/mindustry/annotations/remote/RemoteReadGenerator.java @@ -33,7 +33,7 @@ public class RemoteReadGenerator{ throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException, IOException{ TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC); - classBuilder.addJavadoc(RemoteMethodAnnotationProcessor.autogenWarning); + classBuilder.addJavadoc(RemoteProcess.autogenWarning); //create main method builder MethodSpec.Builder readMethod = MethodSpec.methodBuilder("readPacket") diff --git a/annotations/src/main/java/mindustry/annotations/remote/RemoteWriteGenerator.java b/annotations/src/main/java/mindustry/annotations/remote/RemoteWriteGenerator.java index 19c5d73df3..10d822dd59 100644 --- a/annotations/src/main/java/mindustry/annotations/remote/RemoteWriteGenerator.java +++ b/annotations/src/main/java/mindustry/annotations/remote/RemoteWriteGenerator.java @@ -27,11 +27,11 @@ public class RemoteWriteGenerator{ for(ClassEntry entry : entries){ //create builder TypeSpec.Builder classBuilder = TypeSpec.classBuilder(entry.name).addModifiers(Modifier.PUBLIC); - classBuilder.addJavadoc(RemoteMethodAnnotationProcessor.autogenWarning); + classBuilder.addJavadoc(RemoteProcess.autogenWarning); //add temporary write buffer classBuilder.addField(FieldSpec.builder(ByteBuffer.class, "TEMP_BUFFER", Modifier.STATIC, Modifier.PRIVATE, Modifier.FINAL) - .initializer("ByteBuffer.allocate($1L)", RemoteMethodAnnotationProcessor.maxPacketSize).build()); + .initializer("ByteBuffer.allocate($1L)", RemoteProcess.maxPacketSize).build()); //go through each method entry in this class for(MethodEntry methodEntry : entry.methods){ diff --git a/annotations/src/main/java/mindustry/annotations/util/Selement.java b/annotations/src/main/java/mindustry/annotations/util/Selement.java new file mode 100644 index 0000000000..d7e458a20e --- /dev/null +++ b/annotations/src/main/java/mindustry/annotations/util/Selement.java @@ -0,0 +1,50 @@ +package mindustry.annotations.util; + +import com.squareup.javapoet.*; +import mindustry.annotations.*; + +import javax.lang.model.element.*; +import javax.lang.model.type.*; + +public class Selement{ + public final T e; + + public Selement(T e){ + this.e = e; + } + + public Element up(){ + return e.getEnclosingElement(); + } + + public TypeMirror mirror(){ + return e.asType(); + } + + public TypeName tname(){ + return TypeName.get(mirror()); + } + + public ClassName cname(){ + return ClassName.get((TypeElement)BaseProcessor.typeu.asElement(mirror())); + } + + public String name(){ + return e.getSimpleName().toString(); + } + + @Override + public String toString(){ + return e.toString(); + } + + @Override + public int hashCode(){ + return e.hashCode(); + } + + @Override + public boolean equals(Object o){ + return o != null && o.getClass() == getClass() && ((Selement)o).e.equals(e); + } +} diff --git a/annotations/src/main/java/mindustry/annotations/util/Smethod.java b/annotations/src/main/java/mindustry/annotations/util/Smethod.java new file mode 100644 index 0000000000..b9687dd728 --- /dev/null +++ b/annotations/src/main/java/mindustry/annotations/util/Smethod.java @@ -0,0 +1,48 @@ +package mindustry.annotations.util; + +import arc.struct.*; +import com.squareup.javapoet.*; +import com.sun.source.tree.*; +import mindustry.annotations.*; + +import javax.lang.model.element.*; +import javax.lang.model.type.*; + +public class Smethod extends Selement{ + + public Smethod(ExecutableElement executableElement){ + super(executableElement); + } + + public boolean is(Modifier mod){ + return e.getModifiers().contains(mod); + } + + public Array thrown(){ + return Array.with(e.getThrownTypes()).as(TypeMirror.class); + } + + public Array thrownt(){ + return Array.with(e.getThrownTypes()).map(TypeName::get); + } + + public Array typeVariables(){ + return Array.with(e.getTypeParameters()).as(TypeParameterElement.class); + } + + public Array params(){ + return Array.with(e.getParameters()).map(Svar::new); + } + + public TypeMirror ret(){ + return e.getReturnType(); + } + + public TypeName retn(){ + return TypeName.get(ret()); + } + + public MethodTree tree(){ + return BaseProcessor.trees.getTree(e); + } +} diff --git a/annotations/src/main/java/mindustry/annotations/util/Stype.java b/annotations/src/main/java/mindustry/annotations/util/Stype.java new file mode 100644 index 0000000000..99b9b9f781 --- /dev/null +++ b/annotations/src/main/java/mindustry/annotations/util/Stype.java @@ -0,0 +1,60 @@ +package mindustry.annotations.util; + +import arc.struct.*; +import mindustry.annotations.*; + +import javax.lang.model.element.*; +import javax.lang.model.type.*; +import java.lang.annotation.*; + +public class Stype extends Selement{ + + public Stype(TypeElement typeElement){ + super(typeElement); + } + + public static Stype of(TypeMirror mirror){ + return new Stype((TypeElement)BaseProcessor.typeu.asElement(mirror)); + } + + public Array interfaces(){ + return Array.with(e.getInterfaces()).map(Stype::of); + } + + public Array superclasses(){ + Array out = new Array<>(); + Stype sup = superclass(); + while(!sup.name().equals("Object")){ + out.add(sup); + sup = sup.superclass(); + } + return out; + } + + public Stype superclass(){ + return new Stype((TypeElement)BaseProcessor.typeu.asElement(BaseProcessor.typeu.directSupertypes(mirror()).get(0))); + } + + public A annotation(Class annotation){ + return e.getAnnotation(annotation); + } + + public Array fields(){ + return Array.with(e.getEnclosedElements()).select(e -> e instanceof VariableElement).map(e -> new Svar((VariableElement)e)); + } + + public Array methods(){ + return Array.with(e.getEnclosedElements()).select(e -> e instanceof ExecutableElement + && !e.getSimpleName().toString().contains("<")).map(e -> new Smethod((ExecutableElement)e)); + } + + public Array constructors(){ + return Array.with(e.getEnclosedElements()).select(e -> e instanceof ExecutableElement + && e.getSimpleName().toString().contains("<")).map(e -> new Smethod((ExecutableElement)e)); + } + + @Override + public TypeMirror mirror(){ + return e.asType(); + } +} diff --git a/annotations/src/main/java/mindustry/annotations/util/Svar.java b/annotations/src/main/java/mindustry/annotations/util/Svar.java new file mode 100644 index 0000000000..cf58d7a6f5 --- /dev/null +++ b/annotations/src/main/java/mindustry/annotations/util/Svar.java @@ -0,0 +1,21 @@ +package mindustry.annotations.util; + +import com.sun.source.tree.*; +import mindustry.annotations.*; + +import javax.lang.model.element.*; + +public class Svar extends Selement{ + + public Svar(VariableElement e){ + super(e); + } + + public boolean is(Modifier mod){ + return e.getModifiers().contains(mod); + } + + public VariableTree tree(){ + return (VariableTree)BaseProcessor.trees.getTree(e); + } +} diff --git a/core/assets-raw/sprites/blocks/storage/core-nucleus.png b/core/assets-raw/sprites/blocks/storage/core-nucleus.png index 90c5a6c1f4..d92d7033ea 100644 Binary files a/core/assets-raw/sprites/blocks/storage/core-nucleus.png and b/core/assets-raw/sprites/blocks/storage/core-nucleus.png differ diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 23fe9a4dfa..cc5b2a4873 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -59,6 +59,7 @@ stat.built = Buildings Built:[accent] {0} stat.destroyed = Buildings Destroyed:[accent] {0} stat.deconstructed = Buildings Deconstructed:[accent] {0} stat.delivered = Resources Launched: +stat.playtime = Time Played:[accent] {0} stat.rank = Final Rank: [accent]{0} launcheditems = [accent]Launched Items diff --git a/core/assets/bundles/bundle_it.properties b/core/assets/bundles/bundle_it.properties index 1e9e778507..c142113cc3 100644 --- a/core/assets/bundles/bundle_it.properties +++ b/core/assets/bundles/bundle_it.properties @@ -45,7 +45,7 @@ schematic.exportfile = Esporta File schematic.importfile = Importa File schematic.browseworkshop = Naviga nel Workshop schematic.copy = Copia negli Appunti -schematic.copy.import = Incolla dagli Appunti +schematic.copy.import = Importa dagli Appunti schematic.shareworkshop = Condividi nel Workshop schematic.flip = [accent][[{0}][]/[accent][[{1}][]: Ruota Schematica schematic.saved = Schematica salvata. @@ -59,6 +59,7 @@ stat.built = Costruzioni Erette:[accent] {0} stat.destroyed = Costruzioni Distrutte:[accent] {0} stat.deconstructed = Costruzioni Smantellate:[accent] {0} stat.delivered = Risorse Lanciate: +stat.playtime = Tempo Di Gioco:[accent] {0} stat.rank = Livello Finale: [accent]{0} launcheditems = [accent]Oggetti Lanciati @@ -69,7 +70,7 @@ level.select = Selezione del Livello level.mode = Modalità di Gioco: showagain = Non mostrare più coreattack = < Il Nucleo è sotto attacco! > -nearpoint = [[ [scarlet]LASCIA LA ZONA NEMICA IMMEDIATAMENTE[] ]\autodistruzione imminente +nearpoint = [[ [scarlet]LASCIA LA ZONA NEMICA IMMEDIATAMENTE[] ]\nautodistruzione imminente database = Database Nucleo savegame = Salva loadgame = Carica @@ -104,6 +105,7 @@ mods.none = [lightgray]Nessuna mod trovata! mods.guide = Guida per il modding mods.report = Segnala un Bug mods.openfolder = Apri Cartella Mods +mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Abilitato mod.disabled = [scarlet]Disabilitato mod.disable = Disabilita @@ -143,7 +145,7 @@ server.closing = [accent]Chiusura server... server.kicked.kick = Sei stato espulso dal server! server.kicked.whitelist = Non sei presente nella whitelist. server.kicked.serverClose = Server chiuso. -server.kicked.vote = Sei stato esplso su richiesta dei giocatori. Tanti saluti. +server.kicked.vote = Sei stato espulso su richiesta dei giocatori. Tanti saluti. server.kicked.clientOutdated = Versione del client obsoleta! Aggiorna il gioco! server.kicked.serverOutdated = Server obsoleto! Chiedi all'host di aggiornare la versione del server! server.kicked.banned = Sei stato bandito da questo server. @@ -668,6 +670,7 @@ setting.mutesound.name = Silenzia Suoni setting.crashreport.name = Invia rapporti anonimi sugli arresti anomali setting.savecreate.name = Salvataggio Automatico setting.publichost.name = Gioco Visibile Pubblicamente +setting.playerlimit.name = Limite Giocatori setting.chatopacity.name = Opacità Chat setting.lasersopacity.name = Opacità Raggi Energetici setting.bridgeopacity.name = Opacità Nastri e Condotti Sopraelevati diff --git a/core/assets/bundles/bundle_nl.properties b/core/assets/bundles/bundle_nl.properties index 6a8681f28a..8a45402b26 100644 --- a/core/assets/bundles/bundle_nl.properties +++ b/core/assets/bundles/bundle_nl.properties @@ -40,7 +40,7 @@ schematic = Blauwdruk schematic.add = Bewaar blauwdruk... schematics = Blauwdrukken schematic.replace = Er bestaat al een blauwdruk met die naam. Overschrijven? -schematic.import = Importeer blauwdrul... +schematic.import = Importeer blauwdruk... schematic.exportfile = Exporteer bestand schematic.importfile = Importeer bestand schematic.browseworkshop = Blader Werkplaats @@ -245,13 +245,13 @@ workshop.listing = Bewerk Workshop vermelding ok = Oke open = Open customize = Aanpassen -cancel = Anuleer +cancel = Annuleer openlink = Open Link copylink = Customize Link -back = Teru +back = Terug data.export = Exporteer Data data.import = Importeer Data -data.exported = Data Geexporteerd. +data.exported = Data Geëxporteerd. data.invalid = Dit is geen geldige game data. data.import.confirm = Importeren van data verwijderd[scarlet] alle[] huidige data.\n[accent]Dit kan niet ongedaan worden gemaakt![]\n\nWanneer de data is geimport herstart deze game automatisch. classic.export = Exporteer klassieke data @@ -450,7 +450,7 @@ launch.title = Lancering Sucessvol launch.next = [LIGHT_GRAY]volgende lanceerkans in ronde {0} launch.unable2 = [scarlet]Lanceren niet mogelijk.[] launch.confirm = Dit lanceert alle items in je core.\nJe zal niet meer terug kunnen keren naar deze basis. -launch.skip.confirm = Als je nu niet lanceert, zul je moeten wachten tot het wel weer kan. +launch.skip.confirm = Als je nu niet lanceert zul je moeten wachten tot de volgende mogelijkheid. uncover = Ontdek configure = Configureer startinventaris bannedblocks = Verboden Blokken @@ -532,21 +532,21 @@ blocks.input = Input blocks.output = Output blocks.booster = Booster block.unknown = [LIGHT_GRAY]??? -blocks.powercapacity = Stroom Capaciteit -blocks.powershot = Stroom/Shot -blocks.damage = Damage +blocks.powercapacity = Stroomcapaciteit +blocks.powershot = Stroom/Schot +blocks.damage = Schade blocks.targetsair = Luchtdoelwitten blocks.targetsground = Gronddoelwitten blocks.itemsmoved = Beweegsnelheid blocks.launchtime = Tijd tussen lanceringen blocks.shootrange = Bereik blocks.size = Formaat -blocks.liquidcapacity = Vloeistof Capaciteit -blocks.powerrange = Stroom Bereik -blocks.powerconnections = Maximale Hoeveelheid Dradem -blocks.poweruse = Stroom verbruik -blocks.powerdamage = Stroom/Damage -blocks.itemcapacity = Materiaal Capaciteit +blocks.liquidcapacity = Vloeistofcapaciteit +blocks.powerrange = Stroombereik +blocks.powerconnections = Maximale Hoeveelheid Connecties +blocks.poweruse = Stroomverbruik +blocks.powerdamage = Stroom/Schade +blocks.itemcapacity = Materiaalcapaciteit blocks.basepowergeneration = Standaard Stroom Generatie blocks.productiontime = Productie Tijd blocks.repairtime = Volledige Blok Repareertijd @@ -556,17 +556,17 @@ blocks.drilltier = Valt te delven blocks.drillspeed = Standaard mine snelheid blocks.boosteffect = Boost Effect blocks.maxunits = Maximaal Actieve Units -blocks.health = Health -blocks.buildtime = Bouw tijd -blocks.buildcost = Bouw kosten +blocks.health = Levenspunten +blocks.buildtime = Bouwtijd +blocks.buildcost = Bouwkosten blocks.inaccuracy = Onnauwkeurigheid blocks.shots = Shoten blocks.reload = Schoten/Seconde -blocks.ammo = Ammonutie +blocks.ammo = Ammunitie bar.drilltierreq = Betere miner nodig bar.drillspeed = Mining Snelheid: {0}/s -bar.pumpspeed = Pomp Snelheid: {0}/s +bar.pumpspeed = Pompsnelheid: {0}/s bar.efficiency = Rendement: {0}% bar.powerbalance = Stroom: {0} bar.powerstored = Opgeslagen: {0}/{1} @@ -591,15 +591,15 @@ bullet.frag = [stat]clusterbom bullet.knockback = [stat]{0}[lightgray] terugslag bullet.freezing = [stat]bevriezend bullet.tarred = [stat]pek -bullet.multiplier = [stat]{0}[lightgray]x ammonutie verdubbelaar +bullet.multiplier = [stat]{0}[lightgray]x ammunitieverdubbelaar bullet.reload = [stat]{0}[lightgray]x herlaad unit.blocks = blokken -unit.powersecond = stroom eenheid/seconde -unit.liquidsecond = vloeistof eenheid/seconde +unit.powersecond = stroomeenheid/seconde +unit.liquidsecond = vloeistofeenheid/seconde unit.itemssecond = items/seconde -unit.liquidunits = vloeistof eenheid -unit.powerunits = stroom eenheid +unit.liquidunits = vloeistofeenheid +unit.powerunits = stroomeenheid unit.degrees = graden unit.seconds = secondes unit.persecond = /sec @@ -615,8 +615,8 @@ category.items = Items category.crafting = Productie category.shooting = Wapens category.optional = Optionele Verbeteringen -setting.landscape.name = Vergrendel Landscape -setting.shadows.name = Schaduws +setting.landscape.name = Vergrendel Landschap +setting.shadows.name = Schaduwen setting.blockreplace.name = Automatische Blok Suggesties setting.linear.name = Linear Filtering setting.hints.name = Hints @@ -629,14 +629,14 @@ setting.autotarget.name = Auto-Target setting.keyboard.name = Muis+Toetsenbord Controls setting.touchscreen.name = Touchscreen Controls setting.fpscap.name = Max FPS -setting.fpscap.none = None +setting.fpscap.none = Geen setting.fpscap.text = {0} FPS setting.uiscale.name = UI Schaal[lightgray] (herstart vereist)[] setting.swapdiagonal.name = Altijd Diagonaal Plaatsen -setting.difficulty.training = kalm +setting.difficulty.training = oefening setting.difficulty.easy = makkelijk setting.difficulty.normal = normaal -setting.difficulty.hard = hard +setting.difficulty.hard = moeilijk setting.difficulty.insane = krankzinnig setting.difficulty.name = Moeilijkheidsgraad: setting.screenshake.name = Schuddend Scherm @@ -932,7 +932,7 @@ block.lancer.name = Lancer block.conveyor.name = Lopende Band block.titanium-conveyor.name = Titanium Lopende Band block.armored-conveyor.name = Gepantserde Lopende Band -block.armored-conveyor.description = Verplaatst items met dezelfde snelheid als een van titanium, maar heeft meer levenspunten. accepteert alleen items van de zijkanten als het ook lopende banden zijn. +block.armored-conveyor.description = Verplaatst items met dezelfde snelheid als een van titanium, maar heeft meer levenspunten. Accepteert alleen items van de zijkanten als het ook lopende banden zijn. block.junction.name = Kruising block.router.name = Router block.distributor.name = Distributor @@ -1060,7 +1060,7 @@ unit.eradicator.name = Eradicator unit.lich.name = Lich unit.reaper.name = Reaper tutorial.next = [lightgray] -tutorial.intro = Welkom bij de[scarlet] Mindustry Tutorial.[]\nBegin met het[accent] delven van koper[]. Klik op een vakje die het heeft om het te delven.\n\n[accent]{0}/{1} koper +tutorial.intro = Welkom bij de[scarlet] Mindustry Tutorial.[]\nBegin met het[accent] delven van koper[]. Klik op een vakje met koper om het te delven.\n\n[accent]{0}/{1} koper tutorial.intro.mobile = Welkom bij de[scarlet] Mindustry Tutorial.[]\nVeeg over het scherm om te bewegen.\n[accent]Knijp met 2 vingers [] om in en uit te zoomen.\nBegin met het[accent] delven van koper[]. Beweeg dichterbij, en klik er dan op.\n\n[accent]{0}/{1} koper tutorial.drill = Met de hand delven is inefficient.\n[accent]Drills []kunnen automatisch voor je delven.\nPlaats er een op de koper. tutorial.drill.mobile = Met de hand delven is inefficient.\n[accent]Drills []kunnen automatisch voor je delven.\nZoek de drill rechts onderin.\nSelecter de[accent] mechanische drill[].\nPlaats het op de koper door erop te klikken, druk dan op het[accent] vinkje[] om het bouwen te bevestigen.\nKlik op de[accent] X knop[] om het te anuleren. @@ -1068,8 +1068,8 @@ tutorial.blockinfo = Elk blok heeft andere statistieken. Elke drill kan enkel be tutorial.conveyor = [accent]Lopende Banden[] worden gebruikt om je items naar je core te krijgen.\nLeg een line aan van je drills tot aan je core. tutorial.conveyor.mobile = [accent]Lopende Banden[] worden gebruikt om je items naar je core te krijgen.\nLeg een line aan van je drills tot aan je core.\n[accent] Doe dit door je vinger een paar seconden stil te houden[] en dan in een richting te slepen.\n\n[accent]{0}/{1} lopende banden in 1x geplaatst\n[accent]0/1 items afgeleverd tutorial.turret = Defensieve gebouwen moeten worden gebouwd tegen de[LIGHT_GRAY] vijand[].\nBouw een duo kannon bij je basis. -tutorial.drillturret = Duo's hebben[accent] koperen ammonutie []nodig om te schieten.\nPlaatst een drill ernaast om het van koper te voorzien. -tutorial.pause = Tijdens een gevecht is het mogelijk[accent] het spel te pauzeren.[]\nJe kan nog wel je gebouwen plannen dan.\n\n[accent]Pauzeer het spel (spatie) nu. +tutorial.drillturret = Duo's hebben[accent] koperen ammunitie []nodig om te schieten.\nPlaats een drill ernaast om het van koper te voorzien. +tutorial.pause = Tijdens een gevecht is het mogelijk[accent] het spel te pauzeren.[]\nJe kan nog wel je gebouwen plannen.\n\n[accent]Pauzeer het spel (spatie) nu. tutorial.pause.mobile = During battle, you are able to[accent] pause the game.[]\nYou may queue buildings while paused.\n\n[accent]Press this button in the top left to pause. tutorial.unpause = Doe het opnieuw om weer verder te gaan. tutorial.unpause.mobile = Doe het opnieuw om weer verder te gaan. @@ -1079,10 +1079,10 @@ tutorial.withdraw = In sommige situaties, is het nodig om items uit een blok te tutorial.deposit = Je kan de items weer terugstoppen door van je schip het terug te slepen naar waar je het wilt.\n\n[accent]Stop het nu weer terug in de core.[] tutorial.waves = De[LIGHT_GRAY] vijand[] naderd.\n\nVerdedig je core voor 2 rondes. Bouw meer verdedigingen. tutorial.waves.mobile = De[LIGHT_GRAY] vijand[] naderd.\n\nVerdedig je core voor 2 rondes. Je schip schiet automatisch op vijanden.\nBouw meer verdedigingen, en mine meer koper. -tutorial.launch = Tijdens sommige waves, kan je je core[accent] lanceren[], hiermee verlaat je de basis permanent[accent] maar je neemt wel alles dat in de core zit met je mee.[]\nVervolgens valt ermee te onderzoeken.\n\n[accent]Druk op de lanceer knop. +tutorial.launch = Tijdens sommige waves, kan je je core[accent] lanceren[], hiermee verlaat je de basis permanent[accent] maar je neemt wel alles dat in de core zit met je mee.[]\nMet deze grondstoffen kan je nieuwe technologieën onderzoeken.\n\n[accent]Druk op de lanceerknop. -item.copper.description = A useful structure material. Used extensively in all types of blocks. -item.lead.description = A basic starter material. Used extensively in electronics and liquid transportation blocks. +item.copper.description = Een nuttig materiaal voor gebouwen. Wordt erg vaak in blokken gebruikt. +item.lead.description = Een basismateriaal. Wordt vaak gebruikt in elektronica en vloeistoftransport. item.metaglass.description = A super-tough glass compound. Extensively used for liquid distribution and storage. item.graphite.description = Mineralized carbon, used for ammunition and electrical insulation. item.sand.description = A common material that is used extensively in smelting, both in alloying and as a flux. diff --git a/core/assets/sprites/block_colors.png b/core/assets/sprites/block_colors.png index ce47c0beab..0771587db8 100644 Binary files a/core/assets/sprites/block_colors.png and b/core/assets/sprites/block_colors.png differ diff --git a/core/assets/sprites/sprites.png b/core/assets/sprites/sprites.png index c67b2e4ade..220d7b6b66 100644 Binary files a/core/assets/sprites/sprites.png and b/core/assets/sprites/sprites.png differ diff --git a/core/assets/sprites/sprites3.png b/core/assets/sprites/sprites3.png index f4f4056bb3..620c8eb00c 100644 Binary files a/core/assets/sprites/sprites3.png and b/core/assets/sprites/sprites3.png differ diff --git a/core/assets/sprites/sprites5.png b/core/assets/sprites/sprites5.png index 5e6f1bcf24..1de28ac0fd 100644 Binary files a/core/assets/sprites/sprites5.png and b/core/assets/sprites/sprites5.png differ diff --git a/core/src/mindustry/ai/WaveSpawner.java b/core/src/mindustry/ai/WaveSpawner.java index 3bd5bc5400..d011ce8dd8 100644 --- a/core/src/mindustry/ai/WaveSpawner.java +++ b/core/src/mindustry/ai/WaveSpawner.java @@ -39,7 +39,7 @@ public class WaveSpawner{ /** @return true if the player is near a ground spawn point. */ public boolean playerNear(){ - return groundSpawns.contains(g -> Mathf.dst(g.x * tilesize, g.y * tilesize, player.x, player.y) < state.rules.dropZoneRadius); + return groundSpawns.contains(g -> Mathf.dst(g.x * tilesize, g.y * tilesize, player.x, player.y) < state.rules.dropZoneRadius && player.getTeam() != state.rules.waveTeam); } public void spawnEnemies(){ diff --git a/core/src/mindustry/core/Renderer.java b/core/src/mindustry/core/Renderer.java index 7d5e6714b5..bf0e7bc5f6 100644 --- a/core/src/mindustry/core/Renderer.java +++ b/core/src/mindustry/core/Renderer.java @@ -166,6 +166,13 @@ public class Renderer implements ApplicationListener{ } } + @Override + public void resume(){ + if(settings.getBool("bloom") && bloom != null){ + bloom.resume(); + } + } + void setupBloom(){ try{ if(bloom != null){ diff --git a/core/src/mindustry/entities/Effects.java b/core/src/mindustry/entities/Effects.java index 6f2a04033a..c1f85aceef 100644 --- a/core/src/mindustry/entities/Effects.java +++ b/core/src/mindustry/entities/Effects.java @@ -1,15 +1,14 @@ package mindustry.entities; -import arc.Core; -import arc.struct.Array; -import arc.func.Cons; -import arc.graphics.Color; +import arc.*; +import arc.func.*; +import arc.graphics.*; import arc.graphics.g2d.*; -import arc.math.Mathf; -import arc.math.geom.Position; -import arc.util.pooling.Pools; -import mindustry.entities.type.EffectEntity; -import mindustry.entities.traits.ScaleTrait; +import arc.math.*; +import arc.math.geom.*; +import arc.struct.*; +import arc.util.pooling.*; +import mindustry.entities.type.*; public class Effects{ private static final EffectContainer container = new EffectContainer(); @@ -126,7 +125,7 @@ public class Effects{ } } - public static class EffectContainer implements ScaleTrait{ + public static class EffectContainer implements Scaled{ public float x, y, time, lifetime, rotation; public Color color; public int id; diff --git a/core/src/mindustry/entities/EntityGroup.java b/core/src/mindustry/entities/EntityGroup.java index 24c3f0e4f0..6c4bbea92a 100644 --- a/core/src/mindustry/entities/EntityGroup.java +++ b/core/src/mindustry/entities/EntityGroup.java @@ -125,7 +125,7 @@ public class EntityGroup implements Iterable{ entitiesToAdd.clear(); for(T e : entitiesToRemove){ - entityArray.removeValue(e, true); + entityArray.remove(e, true); if(map != null){ map.remove(e.getID()); } @@ -148,7 +148,7 @@ public class EntityGroup implements Iterable{ }else{ //maybe it's being queued? for(T check : entitiesToAdd){ if(check.getID() == id){ //if it is indeed queued, remove it - entitiesToAdd.removeValue(check, true); + entitiesToAdd.remove(check, true); if(removeListener != null){ removeListener.get(check); } diff --git a/core/src/mindustry/entities/def/EntityComps.java b/core/src/mindustry/entities/def/EntityComps.java new file mode 100644 index 0000000000..3dea444169 --- /dev/null +++ b/core/src/mindustry/entities/def/EntityComps.java @@ -0,0 +1,231 @@ +package mindustry.entities.def; + +import arc.graphics.*; +import arc.math.*; +import arc.math.geom.*; +import arc.struct.Bits; +import arc.struct.*; +import arc.util.*; +import arc.util.pooling.*; +import mindustry.annotations.Annotations.*; +import mindustry.content.*; +import mindustry.ctype.*; +import mindustry.entities.bullet.*; +import mindustry.entities.units.*; +import mindustry.gen.*; +import mindustry.type.*; + +import java.io.*; + +import static mindustry.Vars.content; + +public class EntityComps{ + + @Depends({HealthComp.class, VelComp.class, StatusComp.class}) + class UnitComp{ + + } + + class OwnerComp{ + Entityc owner; + } + + @Depends({TimedComp.class}) + class BulletComp{ + BulletType bullet; + + void init(){ + bullet.init(); + } + } + + abstract class TimedComp extends EntityComp implements Scaled{ + float time, lifetime; + + void update(){ + time = Math.min(time + Time.delta(), lifetime); + + if(time >= lifetime){ + remove(); + } + } + + @Override + public float fin(){ + return time / lifetime; + } + } + + class HealthComp{ + float health, maxHealth; + boolean dead; + + float healthf(){ + return health / maxHealth; + } + } + + abstract class PosComp implements Position{ + float x, y; + + void set(float x, float y){ + this.x = x; + this.y = y; + } + } + + @Depends(PosComp.class) + class VelComp{ + //transient fields act as imports from any other component clases; these are ignored by the generator + transient float x, y; + + final Vec2 vel = new Vec2(); + + void update(){ + x += vel.x; + y += vel.y; + vel.scl(0.9f); + } + } + + @Depends(PosComp.class) + class HitboxComp{ + transient float x, y; + + float hitSize; + + boolean collides(Hitboxc other){ + return Intersector.overlapsRect(x - hitSize/2f, y - hitSize/2f, hitSize, hitSize, + other.getX() - other.getHitSize()/2f, other.getY() - other.getHitSize()/2f, other.getHitSize(), other.getHitSize()); + } + } + + @Depends(PosComp.class) + class StatusComp{ + private Array statuses = new Array<>(); + private Bits applied = new Bits(content.getBy(ContentType.status).size); + + private float speedMultiplier; + private float damageMultiplier; + private float armorMultiplier; + + void apply(StatusEffect effect, float duration){ + if(effect == StatusEffects.none || effect == null || isImmune(effect)) return; //don't apply empty or immune effects + + if(statuses.size > 0){ + //check for opposite effects + for(StatusEntry entry : statuses){ + //extend effect + if(entry.effect == effect){ + entry.time = Math.max(entry.time, duration); + return; + }else if(entry.effect.reactsWith(effect)){ //find opposite + StatusEntry.tmp.effect = entry.effect; + //TODO unit cannot be null here + entry.effect.getTransition(null, effect, entry.time, duration, StatusEntry.tmp); + entry.time = StatusEntry.tmp.time; + + if(StatusEntry.tmp.effect != entry.effect){ + entry.effect = StatusEntry.tmp.effect; + } + + //stop looking when one is found + return; + } + } + } + + //otherwise, no opposites found, add direct effect + StatusEntry entry = Pools.obtain(StatusEntry.class, StatusEntry::new); + entry.set(effect, duration); + statuses.add(entry); + } + + boolean isImmune(StatusEffect effect){ + return false; + } + + Color getStatusColor(){ + if(statuses.size == 0){ + return Tmp.c1.set(Color.white); + } + + float r = 0f, g = 0f, b = 0f; + for(StatusEntry entry : statuses){ + r += entry.effect.color.r; + g += entry.effect.color.g; + b += entry.effect.color.b; + } + return Tmp.c1.set(r / statuses.size, g / statuses.size, b / statuses.size, 1f); + } + + void update(){ + applied.clear(); + speedMultiplier = damageMultiplier = armorMultiplier = 1f; + + if(statuses.isEmpty()) return; + + statuses.eachFilter(entry -> { + entry.time = Math.max(entry.time - Time.delta(), 0); + applied.set(entry.effect.id); + + if(entry.time <= 0){ + Pools.free(entry); + return true; + }else{ + speedMultiplier *= entry.effect.speedMultiplier; + armorMultiplier *= entry.effect.armorMultiplier; + damageMultiplier *= entry.effect.damageMultiplier; + //TODO unit can't be null + entry.effect.update(null, entry.time); + } + + return false; + }); + } + + boolean hasEffect(StatusEffect effect){ + return applied.get(effect.id); + } + + void writeSave(DataOutput stream) throws IOException{ + stream.writeByte(statuses.size); + for(StatusEntry entry : statuses){ + stream.writeByte(entry.effect.id); + stream.writeFloat(entry.time); + } + } + + void readSave(DataInput stream, byte version) throws IOException{ + for(StatusEntry effect : statuses){ + Pools.free(effect); + } + + statuses.clear(); + + byte amount = stream.readByte(); + for(int i = 0; i < amount; i++){ + byte id = stream.readByte(); + float time = stream.readFloat(); + StatusEntry entry = Pools.obtain(StatusEntry.class, StatusEntry::new); + entry.set(content.getByID(ContentType.status, id), time); + statuses.add(entry); + } + } + } + + @BaseComponent + class EntityComp{ + int id; + + void init(){} + + void update(){} + + void remove(){} + + T as(Class type){ + return (T)this; + } + } +} diff --git a/core/src/mindustry/entities/def/EntityDefs.java b/core/src/mindustry/entities/def/EntityDefs.java new file mode 100644 index 0000000000..d538cec4a8 --- /dev/null +++ b/core/src/mindustry/entities/def/EntityDefs.java @@ -0,0 +1,10 @@ +package mindustry.entities.def; + +import mindustry.annotations.Annotations.*; +import mindustry.entities.def.EntityComps.*; + +class EntityDefs{ + + @EntityDef({BulletComp.class, VelComp.class, TimedComp.class}) + class BulletDef{} +} diff --git a/core/src/mindustry/entities/effect/ItemTransfer.java b/core/src/mindustry/entities/effect/ItemTransfer.java index 2018d15a69..e0f0c39cb0 100644 --- a/core/src/mindustry/entities/effect/ItemTransfer.java +++ b/core/src/mindustry/entities/effect/ItemTransfer.java @@ -28,8 +28,7 @@ public class ItemTransfer extends TimedEntity implements DrawTrait{ private Position to; private Runnable done; - public ItemTransfer(){ - } + public ItemTransfer(){} @Remote(called = Loc.server, unreliable = true) public static void transferItemEffect(Item item, float x, float y, Unit to){ diff --git a/core/src/mindustry/entities/traits/ScaleTrait.java b/core/src/mindustry/entities/traits/ScaleTrait.java deleted file mode 100644 index 2a572659b2..0000000000 --- a/core/src/mindustry/entities/traits/ScaleTrait.java +++ /dev/null @@ -1,43 +0,0 @@ -package mindustry.entities.traits; - -import arc.math.Interpolation; - -public interface ScaleTrait{ - /** 0 to 1. */ - float fin(); - - /** 1 to 0 */ - default float fout(){ - return 1f - fin(); - } - - /** 1 to 0 */ - default float fout(Interpolation i){ - return i.apply(fout()); - } - - /** 1 to 0, ending at the specified margin */ - default float fout(float margin){ - float f = fin(); - if(f >= 1f - margin){ - return 1f - (f - (1f - margin)) / margin; - }else{ - return 1f; - } - } - - /** 0 to 1 **/ - default float fin(Interpolation i){ - return i.apply(fin()); - } - - /** 0 to 1 */ - default float finpow(){ - return Interpolation.pow3Out.apply(fin()); - } - - /** 0 to 1 to 0 */ - default float fslope(){ - return (0.5f - Math.abs(fin() - 0.5f)) * 2f; - } -} diff --git a/core/src/mindustry/entities/traits/TimeTrait.java b/core/src/mindustry/entities/traits/TimeTrait.java index cf89d7dca8..f6c3938e3d 100644 --- a/core/src/mindustry/entities/traits/TimeTrait.java +++ b/core/src/mindustry/entities/traits/TimeTrait.java @@ -1,9 +1,9 @@ package mindustry.entities.traits; -import arc.math.Mathf; +import arc.math.*; import arc.util.Time; -public interface TimeTrait extends ScaleTrait, Entity{ +public interface TimeTrait extends Scaled, Entity{ float lifetime(); diff --git a/core/src/mindustry/entities/type/Bullet.java b/core/src/mindustry/entities/type/Bullet.java index f7e676ecc9..6d9a6eb12e 100644 --- a/core/src/mindustry/entities/type/Bullet.java +++ b/core/src/mindustry/entities/type/Bullet.java @@ -16,7 +16,7 @@ import mindustry.world.*; import static mindustry.Vars.*; -public class Bullet extends SolidEntity implements DamageTrait, ScaleTrait, Poolable, DrawTrait, VelocityTrait, TimeTrait, TeamTrait, AbsorbTrait{ +public class Bullet extends SolidEntity implements DamageTrait, Scaled, Poolable, DrawTrait, VelocityTrait, TimeTrait, TeamTrait, AbsorbTrait{ public Interval timer = new Interval(3); private float lifeScl; diff --git a/core/src/mindustry/entities/type/Player.java b/core/src/mindustry/entities/type/Player.java index f9ccb7dfd3..34ce3fcbe3 100644 --- a/core/src/mindustry/entities/type/Player.java +++ b/core/src/mindustry/entities/type/Player.java @@ -36,7 +36,6 @@ import static mindustry.Vars.*; public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ public static final int timerSync = 2; public static final int timerAbility = 3; - public static final int timerTransfer = 4; private static final int timerShootLeft = 0; private static final int timerShootRight = 1; private static final float liftoffBoost = 0.2f; diff --git a/core/src/mindustry/entities/type/TileEntity.java b/core/src/mindustry/entities/type/TileEntity.java index d83c038e70..0814a4ce58 100644 --- a/core/src/mindustry/entities/type/TileEntity.java +++ b/core/src/mindustry/entities/type/TileEntity.java @@ -204,7 +204,7 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{ other.block().onProximityUpdate(other); if(other.entity != null){ - other.entity.proximity.removeValue(tile, true); + other.entity.proximity.remove(tile, true); } } } diff --git a/core/src/mindustry/entities/type/TimedEntity.java b/core/src/mindustry/entities/type/TimedEntity.java index 413b1c3fd5..17b688002d 100644 --- a/core/src/mindustry/entities/type/TimedEntity.java +++ b/core/src/mindustry/entities/type/TimedEntity.java @@ -1,10 +1,9 @@ package mindustry.entities.type; -import arc.util.pooling.Pool.Poolable; -import mindustry.entities.traits.ScaleTrait; -import mindustry.entities.traits.TimeTrait; +import arc.util.pooling.Pool.*; +import mindustry.entities.traits.*; -public abstract class TimedEntity extends BaseEntity implements ScaleTrait, TimeTrait, Poolable{ +public abstract class TimedEntity extends BaseEntity implements TimeTrait, Poolable{ public float time; @Override diff --git a/core/src/mindustry/entities/units/StatusEntry.java b/core/src/mindustry/entities/units/StatusEntry.java new file mode 100644 index 0000000000..1c1f07f9b7 --- /dev/null +++ b/core/src/mindustry/entities/units/StatusEntry.java @@ -0,0 +1,16 @@ +package mindustry.entities.units; + +import mindustry.type.*; + +public class StatusEntry{ + public static final StatusEntry tmp = new StatusEntry(); + + public StatusEffect effect; + public float time; + + public StatusEntry set(StatusEffect effect, float time){ + this.effect = effect; + this.time = time; + return this; + } +} diff --git a/core/src/mindustry/entities/units/Statuses.java b/core/src/mindustry/entities/units/Statuses.java index 637bbf1a55..7fd37abe5b 100644 --- a/core/src/mindustry/entities/units/Statuses.java +++ b/core/src/mindustry/entities/units/Statuses.java @@ -147,14 +147,4 @@ public class Statuses implements Saveable{ } } - public static class StatusEntry{ - public StatusEffect effect; - public float time; - - public StatusEntry set(StatusEffect effect, float time){ - this.effect = effect; - this.time = time; - return this; - } - } } diff --git a/core/src/mindustry/game/Saves.java b/core/src/mindustry/game/Saves.java index cd3389d1f0..09f09f122a 100644 --- a/core/src/mindustry/game/Saves.java +++ b/core/src/mindustry/game/Saves.java @@ -311,7 +311,7 @@ public class Saves{ public void delete(){ file.delete(); - saves.removeValue(this, true); + saves.remove(this, true); if(this == current){ current = null; } diff --git a/core/src/mindustry/graphics/Bloom.java b/core/src/mindustry/graphics/Bloom.java deleted file mode 100644 index 1b7b8f89f9..0000000000 --- a/core/src/mindustry/graphics/Bloom.java +++ /dev/null @@ -1,382 +0,0 @@ -package mindustry.graphics; - -import arc.Core; -import arc.graphics.*; -import arc.graphics.Pixmap.Format; -import arc.graphics.VertexAttributes.Usage; -import arc.graphics.gl.FrameBuffer; -import arc.graphics.gl.Shader; - -/** - * Bloomlib allow easy but efficient way to add bloom effect as post process - * effect - * - * @author kalle_h - */ -public class Bloom{ - /** - * To use implement bloom more like a glow. Texture alpha channel can be - * used as mask which part are glowing and which are not. see more info at: - * http://www.gamasutra.com/view/feature/2107/realtime_glow.php - *

- * NOTE: need to be set before bloom instance is created. After that this - * does nothing. - */ - public static boolean useAlphaChannelAsMask = false; - - /** how many blur pass */ - public int blurPasses = 1; - - private Shader tresholdShader; - private Shader bloomShader; - - private Mesh fullScreenQuad; - - private Texture pingPongTex1; - private Texture pingPongTex2; - private Texture original; - - private FrameBuffer frameBuffer; - private FrameBuffer pingPongBuffer1; - private FrameBuffer pingPongBuffer2; - - private Shader blurShader; - - private float bloomIntensity; - private float originalIntensity; - private float threshold; - private int w; - private int h; - private boolean blending = false; - private boolean capturing = false; - private float r = 0f; - private float g = 0f; - private float b = 0f; - private float a = 1f; - private boolean disposeFBO = true; - - /** - * IMPORTANT NOTE CALL THIS WHEN RESUMING - */ - public void resume(){ - bloomShader.begin(); - bloomShader.setUniformi("u_texture0", 0); - bloomShader.setUniformi("u_texture1", 1); - bloomShader.end(); - - setSize(w, h); - setThreshold(threshold); - setBloomIntesity(bloomIntensity); - setOriginalIntesity(originalIntensity); - - original = frameBuffer.getTexture(); - pingPongTex1 = pingPongBuffer1.getTexture(); - pingPongTex2 = pingPongBuffer2.getTexture(); - } - - /** - * Initialize bloom class that capsulate original scene capturate, - * tresholding, gaussian blurring and blending. Default values: depth = true - * blending = false 32bits = true - */ - public Bloom(){ - initialize(Core.graphics.getWidth() / 4, Core.graphics.getHeight() / 4, null, false, false, true); - } - - public Bloom(boolean useBlending){ - initialize(Core.graphics.getWidth() / 4, Core.graphics.getHeight() / 4, null, false, useBlending, true); - } - - /** - * Initialize bloom class that capsulate original scene capturate, - * tresholding, gaussian blurring and blending. - * - * @param FBO_W - * @param FBO_H how big fbo is used for bloom texture, smaller = more blur and - * lot faster but aliasing can be problem - * @param hasDepth do rendering need depth buffer - * @param useBlending does fbo need alpha channel and is blending enabled when final - * image is rendered. This allow to combine background graphics - * and only do blooming on certain objects param use32bitFBO does - * fbo use higher precision than 16bits. - */ - public Bloom(int FBO_W, int FBO_H, boolean hasDepth, boolean useBlending, boolean use32bitFBO){ - initialize(FBO_W, FBO_H, null, hasDepth, useBlending, use32bitFBO); - - } - - /** - * EXPERT FUNCTIONALITY. no error checking. Use this only if you know what - * you are doing. Remember that bloom.capture() clear the screen so use - * continue instead if that is a problem. - *

- * Initialize bloom class that capsulate original scene capturate, - * tresholding, gaussian blurring and blending. - *

- * * @param sceneIsCapturedHere diposing is user responsibility. - * - * @param FBO_W - * @param FBO_H how big fbo is used for bloom texture, smaller = more blur and - * lot faster but aliasing can be problem - * @param useBlending does fbo need alpha channel and is blending enabled when final - * image is rendered. This allow to combine background graphics - * and only do blooming on certain objects param use32bitFBO does - * fbo use higher precision than 16bits. - */ - public Bloom(int FBO_W, int FBO_H, FrameBuffer sceneIsCapturedHere, boolean useBlending, boolean use32bitFBO){ - initialize(FBO_W, FBO_H, sceneIsCapturedHere, false, useBlending, use32bitFBO); - disposeFBO = false; - } - - private void initialize(int FBO_W, int FBO_H, FrameBuffer fbo, boolean hasDepth, boolean useBlending, boolean use32bitFBO){ - blending = useBlending; - Format format; - - if(use32bitFBO){ - if(useBlending){ - format = Format.RGBA8888; - }else{ - format = Format.RGB888; - } - - }else{ - if(useBlending){ - format = Format.RGBA4444; - }else{ - format = Format.RGB565; - } - } - if(fbo == null){ - frameBuffer = new FrameBuffer(format, Core.graphics.getWidth(), Core.graphics.getHeight(), hasDepth); - }else{ - frameBuffer = fbo; - } - - pingPongBuffer1 = new FrameBuffer(format, FBO_W, FBO_H, false); - - pingPongBuffer2 = new FrameBuffer(format, FBO_W, FBO_H, false); - - original = frameBuffer.getTexture(); - pingPongTex1 = pingPongBuffer1.getTexture(); - pingPongTex2 = pingPongBuffer2.getTexture(); - - fullScreenQuad = createFullScreenQuad(); - final String alpha = useBlending ? "alpha_" : ""; - - bloomShader = createShader("screenspace", alpha + "bloom"); - - if(useAlphaChannelAsMask){ - tresholdShader = createShader("screenspace", "maskedtreshold"); - }else{ - tresholdShader = createShader("screenspace", alpha + "threshold"); - } - - blurShader = createShader("blurspace", alpha + "gaussian"); - - setSize(FBO_W, FBO_H); - setBloomIntesity(2.5f); - setOriginalIntesity(0.8f); - setThreshold(0.5f); - - bloomShader.begin(); - bloomShader.setUniformi("u_texture0", 0); - bloomShader.setUniformi("u_texture1", 1); - bloomShader.end(); - } - - /** - * Set clearing color for capturing buffer - * - * @param r - * @param g - * @param b - * @param a - */ - public void setClearColor(float r, float g, float b, float a){ - this.r = r; - this.g = g; - this.b = b; - this.a = a; - } - - /** - * Call this before rendering scene. - */ - public void capture(){ - if(!capturing){ - capturing = true; - frameBuffer.begin(); - Gl.clearColor(r, g, b, a); - Gl.clear(Gl.colorBufferBit); - - } - } - - /** - * Pause capturing to fbo. - */ - public void capturePause(){ - if(capturing){ - capturing = false; - frameBuffer.end(); - } - } - - /** Start capturing again after pause, no clearing is done to framebuffer */ - public void captureContinue(){ - if(!capturing){ - capturing = true; - frameBuffer.begin(); - } - } - - /** - * Call this after scene. Renders the bloomed scene. - */ - public void render(){ - if(capturing){ - capturing = false; - frameBuffer.end(); - } - - Gl.disable(Gl.blend); - Gl.disable(Gl.depthTest); - Gl.depthMask(false); - - gaussianBlur(); - - if(blending){ - Gl.enable(Gl.blend); - Gl.blendFunc(Gl.srcAlpha, Gl.oneMinusSrcAlpha); - } - - pingPongTex1.bind(1); - original.bind(0); - - bloomShader.begin(); - fullScreenQuad.render(bloomShader, Gl.triangleFan); - bloomShader.end(); - - } - - private void gaussianBlur(){ - - // cut bright areas of the picture and blit to smaller fbo - - original.bind(0); - pingPongBuffer1.begin(); - tresholdShader.begin(); - fullScreenQuad.render(tresholdShader, Gl.triangleFan, 0, 4); - tresholdShader.end(); - pingPongBuffer1.end(); - - for(int i = 0; i < blurPasses; i++){ - - pingPongTex1.bind(0); - - // horizontal - pingPongBuffer2.begin(); - blurShader.begin(); - blurShader.setUniformf("dir", 1f, 0f); - fullScreenQuad.render(blurShader, Gl.triangleFan, 0, 4); - blurShader.end(); - pingPongBuffer2.end(); - - pingPongTex2.bind(0); - // vertical - pingPongBuffer1.begin(); - blurShader.begin(); - blurShader.setUniformf("dir", 0f, 1f); - fullScreenQuad.render(blurShader, Gl.triangleFan, 0, 4); - blurShader.end(); - pingPongBuffer1.end(); - } - } - - /** - * set intensity for bloom. higher mean more brightening for spots that are - * over threshold - * - * @param intensity multiplier for blurred texture in combining phase. must be - * positive. - */ - public void setBloomIntesity(float intensity){ - bloomIntensity = intensity; - bloomShader.begin(); - bloomShader.setUniformf("BloomIntensity", intensity); - bloomShader.end(); - } - - /** - * set intensity for original scene. under 1 mean darkening and over 1 means - * lightening - * - * @param intensity multiplier for captured texture in combining phase. must be - * positive. - */ - public void setOriginalIntesity(float intensity){ - originalIntensity = intensity; - bloomShader.begin(); - bloomShader.setUniformf("OriginalIntensity", intensity); - bloomShader.end(); - } - - /** - * Treshold for bright parts. everything under threshold is clamped to 0 - * - * @param threshold must be in range 0..1 - */ - public void setThreshold(float threshold){ - this.threshold = threshold; - tresholdShader.begin(); - tresholdShader.setUniformf("threshold", threshold, 1f / (1 - threshold)); - tresholdShader.end(); - } - - private void setSize(int FBO_W, int FBO_H){ - w = FBO_W; - h = FBO_H; - blurShader.begin(); - blurShader.setUniformf("size", FBO_W, FBO_H); - blurShader.end(); - } - - /** - * Call this when application is exiting. - */ - public void dispose(){ - try{ - if(disposeFBO) frameBuffer.dispose(); - - fullScreenQuad.dispose(); - - pingPongBuffer1.dispose(); - pingPongBuffer2.dispose(); - - blurShader.dispose(); - bloomShader.dispose(); - tresholdShader.dispose(); - }catch(Throwable ignored){ - - } - } - - private static Mesh createFullScreenQuad(){ - float[] verts = {-1, -1, 0, 0, 1, -1, 1, 0, 1, 1, 1, 1, -1, 1, 0, 1}; - Mesh tmpMesh = new Mesh(true, 4, 0, - new VertexAttribute(Usage.position, 2, "a_position"), - new VertexAttribute(Usage.textureCoordinates, 2, "a_texCoord0") - ); - - tmpMesh.setVertices(verts); - return tmpMesh; - - } - - private static Shader createShader(String vertexName, String fragmentName){ - String vertexShader = Core.files.internal("bloomshaders/" + vertexName + ".vertex.glsl").readString(); - String fragmentShader = Core.files.internal("bloomshaders/" + fragmentName + ".fragment.glsl").readString(); - return new Shader(vertexShader, fragmentShader); - } - -} diff --git a/core/src/mindustry/graphics/OverlayRenderer.java b/core/src/mindustry/graphics/OverlayRenderer.java index 6cb4348c23..a1d01b1fc1 100644 --- a/core/src/mindustry/graphics/OverlayRenderer.java +++ b/core/src/mindustry/graphics/OverlayRenderer.java @@ -125,7 +125,7 @@ public class OverlayRenderer{ if(tile != null && tile.block() != Blocks.air && tile.getTeam() == player.getTeam()){ tile.block().drawSelect(tile); - if(Core.input.keyDown(Binding.rotateplaced) && tile.block().rotate){ + if(Core.input.keyDown(Binding.rotateplaced) && tile.block().rotate && tile.interactable(player.getTeam())){ control.input.drawArrow(tile.block(), tile.x, tile.y, tile.rotation(), true); Draw.color(Pal.accent, 0.3f + Mathf.absin(4f, 0.2f)); Fill.square(tile.drawx(), tile.drawy(), tile.block().size * tilesize/2f); diff --git a/core/src/mindustry/input/DesktopInput.java b/core/src/mindustry/input/DesktopInput.java index 0776d9e33e..b5c8f4a823 100644 --- a/core/src/mindustry/input/DesktopInput.java +++ b/core/src/mindustry/input/DesktopInput.java @@ -222,7 +222,7 @@ public class DesktopInput extends InputHandler{ cursorType = ui.unloadCursor; } - if(!isPlacing() && Math.abs(Core.input.axisTap(Binding.rotate)) > 0 && Core.input.keyDown(Binding.rotateplaced) && cursor.block().rotate){ + if(cursor.interactable(player.getTeam()) && !isPlacing() && Math.abs(Core.input.axisTap(Binding.rotate)) > 0 && Core.input.keyDown(Binding.rotateplaced) && cursor.block().rotate){ Call.rotateBlock(player, cursor, Core.input.axisTap(Binding.rotate) > 0); } } diff --git a/core/src/mindustry/input/MobileInput.java b/core/src/mindustry/input/MobileInput.java index 177ecd78b2..5c8619df48 100644 --- a/core/src/mindustry/input/MobileInput.java +++ b/core/src/mindustry/input/MobileInput.java @@ -156,7 +156,7 @@ public class MobileInput extends InputHandler implements GestureListener{ } void removeRequest(BuildRequest request){ - selectRequests.removeValue(request, true); + selectRequests.remove(request, true); if(!request.breaking){ removals.add(request); } diff --git a/core/src/mindustry/mod/Scripts.java b/core/src/mindustry/mod/Scripts.java index 6d70437dbb..94acd92eb4 100644 --- a/core/src/mindustry/mod/Scripts.java +++ b/core/src/mindustry/mod/Scripts.java @@ -12,7 +12,7 @@ import org.mozilla.javascript.*; public class Scripts implements Disposable{ private final Array blacklist = Array.with("net", "files", "reflect", "javax", "rhino", "file", "channels", "jdk", "runtime", "util.os", "rmi", "security", "org.", "sun.", "beans", "sql", "http", "exec", "compiler", "process", "system", - ".awt", "socket", "classloader", "oracle"); + ".awt", "socket", "classloader", "oracle", "invoke"); private final Array whitelist = Array.with("mindustry.net"); private final Context context; private final String wrapper; diff --git a/core/src/mindustry/net/Administration.java b/core/src/mindustry/net/Administration.java index b48080484d..cfd3423608 100644 --- a/core/src/mindustry/net/Administration.java +++ b/core/src/mindustry/net/Administration.java @@ -41,7 +41,6 @@ public class Administration{ if(player.getInfo().messageInfractions >= Config.messageSpamKick.num() && Config.messageSpamKick.num() != 0){ player.con.kick("You have been kicked for spamming.", 1000 * 60 * 2); } - player.getInfo().lastSentMessage = message; return null; }else{ player.getInfo().messageInfractions = 0; @@ -191,7 +190,7 @@ public class Administration{ } } - bannedIPs.removeValue(ip, false); + bannedIPs.remove(ip, false); if(found){ save(); diff --git a/core/src/mindustry/type/StatusEffect.java b/core/src/mindustry/type/StatusEffect.java index 1ade3af60f..ef86ae6176 100644 --- a/core/src/mindustry/type/StatusEffect.java +++ b/core/src/mindustry/type/StatusEffect.java @@ -10,7 +10,7 @@ import mindustry.ctype.ContentType; import mindustry.entities.*; import mindustry.entities.Effects.*; import mindustry.entities.type.*; -import mindustry.entities.units.Statuses.*; +import mindustry.entities.units.*; public class StatusEffect extends MappableContent{ /** Damage dealt by the unit with the effect. */ diff --git a/core/src/mindustry/ui/dialogs/GameOverDialog.java b/core/src/mindustry/ui/dialogs/GameOverDialog.java index bb9b4fcc8d..f5a9605a82 100644 --- a/core/src/mindustry/ui/dialogs/GameOverDialog.java +++ b/core/src/mindustry/ui/dialogs/GameOverDialog.java @@ -62,6 +62,10 @@ public class GameOverDialog extends FloatingDialog{ t.row(); t.add(Core.bundle.format("stat.deconstructed", state.stats.buildingsDeconstructed)); t.row(); + if(control.saves.getCurrent() != null){ + t.add(Core.bundle.format("stat.playtime", control.saves.getCurrent().getPlayTime())); + t.row(); + } if(world.isZone() && !state.stats.itemsDelivered.isEmpty()){ t.add("$stat.delivered"); t.row(); diff --git a/core/src/mindustry/ui/dialogs/JoinDialog.java b/core/src/mindustry/ui/dialogs/JoinDialog.java index 316c4d7e1f..d69a4c61e4 100644 --- a/core/src/mindustry/ui/dialogs/JoinDialog.java +++ b/core/src/mindustry/ui/dialogs/JoinDialog.java @@ -157,7 +157,7 @@ public class JoinDialog extends FloatingDialog{ inner.addImageButton(Icon.trash, Styles.emptyi, () -> { ui.showConfirm("$confirm", "$server.delete", () -> { - servers.removeValue(server, true); + servers.remove(server, true); saveServers(); setupRemote(); refreshRemote(); diff --git a/core/src/mindustry/ui/dialogs/ModsDialog.java b/core/src/mindustry/ui/dialogs/ModsDialog.java index 8320c9eab9..ca89f3a6a6 100644 --- a/core/src/mindustry/ui/dialogs/ModsDialog.java +++ b/core/src/mindustry/ui/dialogs/ModsDialog.java @@ -87,7 +87,7 @@ public class ModsDialog extends FloatingDialog{ void modError(Throwable error){ ui.loadfrag.hide(); - if(Strings.getCauses(error).contains(t -> t.getMessage() != null && (t.getMessage().contains("SSL") || t.getMessage().contains("protocol")))){ + if(Strings.getCauses(error).contains(t -> t.getMessage() != null && (t.getMessage().contains("trust anchor") || t.getMessage().contains("SSL") || t.getMessage().contains("protocol")))){ ui.showErrorMessage("$feature.unsupported"); }else{ ui.showException(error); diff --git a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java index 05c1a08b11..2b4e336d67 100644 --- a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java +++ b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java @@ -144,7 +144,7 @@ public class SettingsMenuDialog extends SettingsDialog{ } }))); - if(!ios){ + if(!mobile){ t.row(); t.addImageTextButton("$data.openfolder", Icon.folder, style, () -> Core.app.openFolder(Core.settings.getDataDirectory().absolutePath())); } diff --git a/core/src/mindustry/world/modules/ItemModule.java b/core/src/mindustry/world/modules/ItemModule.java index 3fe28f90a9..87cc30d468 100644 --- a/core/src/mindustry/world/modules/ItemModule.java +++ b/core/src/mindustry/world/modules/ItemModule.java @@ -12,6 +12,9 @@ public class ItemModule extends BlockModule{ private int[] items = new int[content.items().size]; private int total; + // Make the take() loop persistent so it does not return the same item twice in a row unless there is nothing else to return. + protected int takeRotation; + public void forEach(ItemConsumer cons){ for(int i = 0; i < items.length; i++){ if(items[i] > 0){ @@ -66,21 +69,27 @@ public class ItemModule extends BlockModule{ return total; } - public Item first(){ + public Item first(){ // fixme: entangle with take() for(int i = 0; i < items.length; i++){ - if(items[i] > 0) return content.item(i); + if(items[i] > 0){ + return content.item(i); + } } return null; } public Item take(){ - if(first() == null) return null; - - int id = first().id; - items[id]--; - total--; - - return content.item(id); + for(int i = 0; i < items.length; i++){ + int index = (i + takeRotation); + if(index >= items.length) index -= items.length; //conditional instead of mod + if(items[index] > 0){ + items[index] --; + total --; + takeRotation = index + 1; + return content.item(index % items.length); + } + } + return null; } public int get(Item item){ diff --git a/gradle.properties b/gradle.properties index e3312d11f3..e54e317005 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xms256m -Xmx1024m -archash=649641d8936160221ce24c47f5b0ad10606de289 +archash=1817bb22ac7680700fe780816940f4217a1f7e07 diff --git a/server/src/mindustry/server/ServerControl.java b/server/src/mindustry/server/ServerControl.java index 0ee88e19e2..631b561b42 100644 --- a/server/src/mindustry/server/ServerControl.java +++ b/server/src/mindustry/server/ServerControl.java @@ -653,18 +653,10 @@ public class ServerControl implements ApplicationListener{ }); handler.register("unban", "", "Completely unban a person by IP or ID.", arg -> { - if(arg[0].contains(".")){ - if(netServer.admins.unbanPlayerIP(arg[0])){ - info("Unbanned player by IP: {0}.", arg[0]); - }else{ - err("That IP is not banned!"); - } + if(netServer.admins.unbanPlayerIP(arg[0]) || netServer.admins.unbanPlayerID(arg[0])){ + info("Unbanned player.", arg[0]); }else{ - if(netServer.admins.unbanPlayerID(arg[0])){ - info("Unbanned player by ID: {0}.", arg[0]); - }else{ - err("That ID is not banned!"); - } + err("That IP/ID is not banned!"); } }); diff --git a/servers.json b/servers.json index cada39c4c8..8f6e4d3491 100644 --- a/servers.json +++ b/servers.json @@ -10,5 +10,17 @@ }, { "address": "mindustry.ru" + }, + { + "address": "mindustry.io" + }, + { + "address": "mindustry.io:1000" + }, + { + "address": "mindustry.io:2000" + }, + { + "address": "mindustry.io:3000" } ]