Logic stuff

This commit is contained in:
Anuken
2020-08-07 23:01:40 -04:00
parent 51af6eeec9
commit 8411cc16f1
27 changed files with 874 additions and 665 deletions
@@ -128,11 +128,11 @@ public class Annotations{
String fallback() default "error"; String fallback() default "error";
} }
/** Registers a logic node's slot. */ /** Registers a statement for auto serialization. */
@Target(ElementType.FIELD) @Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface Slot{ public @interface RegisterStatement{
boolean input() default false; String value();
} }
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@@ -0,0 +1,98 @@
package mindustry.annotations.misc;
import arc.func.*;
import arc.struct.*;
import com.squareup.javapoet.*;
import mindustry.annotations.Annotations.*;
import mindustry.annotations.*;
import mindustry.annotations.util.*;
import javax.annotation.processing.*;
import javax.lang.model.element.*;
@SupportedAnnotationTypes("mindustry.annotations.Annotations.RegisterStatement")
public class LogicStatementProcessor extends BaseProcessor{
@Override
public void process(RoundEnvironment env) throws Exception{
TypeSpec.Builder type = TypeSpec.classBuilder("LogicIO")
.addModifiers(Modifier.PUBLIC);
MethodSpec.Builder writer = MethodSpec.methodBuilder("write")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(Object.class, "obj")
.addParameter(StringBuilder.class, "out");
MethodSpec.Builder reader = MethodSpec.methodBuilder("read")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(tname("mindustry.logic.LStatement"))
.addParameter(String[].class, "tokens");
Seq<Stype> types = types(RegisterStatement.class);
type.addField(FieldSpec.builder(
ParameterizedTypeName.get(
ClassName.get(Seq.class),
ParameterizedTypeName.get(ClassName.get(Prov.class),
tname("mindustry.logic.LStatement"))), "allStatements", Modifier.PUBLIC, Modifier.STATIC)
.initializer("Seq.with(" + types.toString(", ", t -> "" + t.toString() + "::new") + ")").build());
boolean beganWrite = false, beganRead = false;
for(Stype c : types){
String name = c.annotation(RegisterStatement.class).value();
if(beganWrite){
writer.nextControlFlow("else if(obj instanceof $T)", c.mirror());
}else{
writer.beginControlFlow("if(obj instanceof $T)", c.mirror());
beganWrite = true;
}
//write the name & individual fields
writer.addStatement("out.append($S)", name);
Seq<Svar> fields = c.fields();
String readSt = "if(tokens[0].equals($S))";
if(beganRead){
reader.nextControlFlow("else " + readSt, name);
}else{
reader.beginControlFlow(readSt, name);
beganRead = true;
}
reader.addStatement("$T result = new $T()", c.mirror(), c.mirror());
for(int i = 0; i < fields.size; i++){
Svar field = fields.get(i);
if(field.is(Modifier.TRANSIENT)) continue;
writer.addStatement("out.append(\" \")");
writer.addStatement("out.append((($T)obj).$L)", c.mirror(), field.name());
//reading primitives, strings and enums is supported; nothing else is
reader.addStatement("result.$L = $L(tokens[$L])",
field.name(),
field.mirror().toString().equals("java.lang.String") ?
"" : (field.tname().isPrimitive() ? field.tname().box().toString() :
field.mirror().toString()) + ".valueOf", //if it's not a string, it must have a valueOf method
i + 1
);
}
reader.addStatement("return result");
}
reader.endControlFlow();
writer.endControlFlow();
reader.addStatement("return null");
type.addMethod(writer.build());
type.addMethod(reader.build());
write(type);
}
}
@@ -0,0 +1 @@
{version:3,fields:[{name:collided,type:arc.struct.IntSeq,size:-1},{name:damage,type:float,size:4},{name:data,type:java.lang.Object,size:-1},{name:lifetime,type:float,size:4},{name:owner,type:Entityc,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:time,type:float,size:4},{name:type,type:mindustry.entities.bullet.BulletType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
@@ -0,0 +1 @@
{version:4,fields:[{name:collided,type:arc.struct.IntSeq,size:-1},{name:damage,type:float,size:4},{name:data,type:java.lang.Object,size:-1},{name:lifetime,type:float,size:4},{name:owner,type:Entityc,size:-1},{name:team,type:mindustry.game.Team,size:-1},{name:time,type:float,size:4},{name:type,type:mindustry.entities.bullet.BulletType,size:-1},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
@@ -0,0 +1 @@
{version:3,fields:[{name:color,type:arc.graphics.Color,size:-1},{name:data,type:java.lang.Object,size:-1},{name:effect,type:mindustry.entities.Effect,size:-1},{name:lifetime,type:float,size:4},{name:offsetX,type:float,size:4},{name:offsetY,type:float,size:4},{name:parent,type:Posc,size:-1},{name:rotation,type:float,size:4},{name:time,type:float,size:4},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
@@ -0,0 +1 @@
{version:4,fields:[{name:color,type:arc.graphics.Color,size:-1},{name:data,type:java.lang.Object,size:-1},{name:effect,type:mindustry.entities.Effect,size:-1},{name:lifetime,type:float,size:4},{name:offsetX,type:float,size:4},{name:offsetY,type:float,size:4},{name:parent,type:Posc,size:-1},{name:rotation,type:float,size:4},{name:time,type:float,size:4},{name:x,type:float,size:4},{name:y,type:float,size:4}]}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 836 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 836 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 777 B

After

Width:  |  Height:  |  Size: 770 B

File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 KiB

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 385 KiB

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 391 KiB

After

Width:  |  Height:  |  Size: 398 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

+1 -1
View File
@@ -1890,7 +1890,7 @@ public class Blocks implements ContentList{
logicProcessor = new LogicProcessor("logic-processor"){{ logicProcessor = new LogicProcessor("logic-processor"){{
requirements(Category.effect, BuildVisibility.debugOnly, with(Items.copper, 200, Items.lead, 100)); requirements(Category.effect, BuildVisibility.debugOnly, with(Items.copper, 200, Items.lead, 100));
size = 3; size = 2;
alwaysUnlocked = true; alwaysUnlocked = true;
}}; }};
+39 -25
View File
@@ -1,12 +1,14 @@
package mindustry.logic; package mindustry.logic;
import arc.func.*;
import arc.struct.*; import arc.struct.*;
import mindustry.io.*; import mindustry.gen.*;
import mindustry.logic.LCanvas.*;
import mindustry.logic.LExecutor.*; import mindustry.logic.LExecutor.*;
/** "Compiles" a sequence of statements into instructions. */ /** "Compiles" a sequence of statements into instructions. */
public class LAssembler{ public class LAssembler{
public static ObjectMap<String, Func<String[], LStatement>> customParsers = new ObjectMap<>();
private transient int lastVar; private transient int lastVar;
/** Maps names to variable IDs. */ /** Maps names to variable IDs. */
ObjectMap<String, BVar> vars = new ObjectMap<>(); ObjectMap<String, BVar> vars = new ObjectMap<>();
@@ -20,35 +22,47 @@ public class LAssembler{
putConst("null", null); putConst("null", null);
} }
public static LAssembler assemble(Seq<StatementElem> seq){ public static LAssembler assemble(String data){
LAssembler out = new LAssembler();
seq.each(s -> s.st.saveUI());
out.instructions = seq.map(s -> s.st.build(out)).toArray(LInstruction.class);
return out;
}
public static String toJson(Seq<StatementElem> seq){
seq.each(s -> s.st.saveUI());
LStatement[] statements = seq.map(s -> s.st).toArray(LStatement.class);
return JsonIO.write(statements);
}
//TODO this is awful and confusing
public static LAssembler fromJson(String json){
LAssembler asm = new LAssembler(); LAssembler asm = new LAssembler();
LStatement[] statements = JsonIO.read(LStatement[].class, json); Seq<LStatement> st = read(data);
for(LStatement s : statements){
s.setupUI();
}
asm.instructions = Seq.with(statements).map(l -> l.build(asm)).toArray(LInstruction.class);
asm.instructions = st.map(l -> l.build(asm)).toArray(LInstruction.class);
return asm; return asm;
} }
public static String write(Seq<LStatement> statements){
StringBuilder out = new StringBuilder();
for(LStatement s : statements){
s.write(out);
out.append("\n");
}
return out.toString();
}
public static Seq<LStatement> read(String data){
Seq<LStatement> statements = new Seq<>();
String[] lines = data.split("[;\n]+");
for(String line : lines){
//comments
if(line.startsWith("#")) continue;
String[] tokens = line.split(" ");
LStatement st = LogicIO.read(tokens);
if(st != null){
statements.add(st);
}else{
//attempt parsing using custom parser if a match is found - this is for mods
String first = tokens[0];
if(customParsers.containsKey(first)){
statements.add(customParsers.get(first).get(tokens));
}
}
}
return statements;
}
/** @return a variable ID by name. /** @return a variable ID by name.
* This may be a constant variable referring to a number or object. */ * This may be a constant variable referring to a number or object. */
public int var(String symbol){ public int var(String symbol){
+69 -18
View File
@@ -16,9 +16,9 @@ import arc.util.*;
import arc.util.ArcAnnotate.*; import arc.util.ArcAnnotate.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.graphics.*; import mindustry.graphics.*;
import mindustry.io.*;
import mindustry.logic.LStatements.*; import mindustry.logic.LStatements.*;
import mindustry.ui.*; import mindustry.ui.*;
import mindustry.ui.dialogs.*;
public class LCanvas extends Table{ public class LCanvas extends Table{
private static final Color backgroundCol = Pal.darkMetal.cpy().mul(0.1f), gridCol = Pal.darkMetal.cpy().mul(0.5f); private static final Color backgroundCol = Pal.darkMetal.cpy().mul(0.1f), gridCol = Pal.darkMetal.cpy().mul(0.5f);
@@ -35,8 +35,30 @@ public class LCanvas extends Table{
pane(statements).grow().get().setClip(false); pane(statements).grow().get().setClip(false);
row();
button("@add", Icon.add, Styles.cleart, () -> {
BaseDialog dialog = new BaseDialog("@add");
dialog.cont.pane(t -> {
t.background(Tex.button);
for(Prov<LStatement> prov : LogicIO.allStatements){
LStatement example = prov.get();
t.button(example.name(), Styles.cleart, () -> {
add(prov.get());
dialog.hide();
}).size(200f, 50f);
t.row();
}
});
dialog.addCloseButton();
dialog.show();
}).height(50f).left().width(400f).marginLeft(10f);
add(new PrintStatement()); add(new PrintStatement());
add(new AssignStatement()); add(new SetStatement());
add(new JumpStatement());
add(new EnableStatement());
add(new OpStatement());
} }
private void drawGrid(){ private void drawGrid(){
@@ -70,18 +92,26 @@ public class LCanvas extends Table{
} }
String save(){ String save(){
return LAssembler.toJson(statements.getChildren().as()); Seq<LStatement> st = statements.getChildren().<StatementElem>as().map(s -> s.st);
st.each(LStatement::saveUI);
return LAssembler.write(st);
} }
void load(String json){ void load(String asm){
statements.clearChildren(); statements.clearChildren();
LStatement[] statements = JsonIO.read(LStatement[].class, json); try{
for(LStatement st : statements){ Seq<LStatement> statements = LAssembler.read(asm);
add(st); for(LStatement st : statements){
} add(st);
}
for(LStatement st : statements){ for(LStatement st : statements){
st.setupUI(); st.setupUI();
}
}catch(Exception e){
//ignore errors reading asm
e.printStackTrace();
} }
this.statements.layout(); this.statements.layout();
@@ -96,7 +126,7 @@ public class LCanvas extends Table{
public class DragLayout extends WidgetGroup{ public class DragLayout extends WidgetGroup{
float margin = 4f; float margin = 4f;
float space = 10f, width = 400f; float space = 10f, width = 400f, prefWidth, prefHeight;
Seq<Element> seq = new Seq<>(); Seq<Element> seq = new Seq<>();
int insertPosition = 0; int insertPosition = 0;
@@ -105,6 +135,8 @@ public class LCanvas extends Table{
float cy = 0; float cy = 0;
seq.clear(); seq.clear();
prefWidth = width;
//layout everything normally //layout everything normally
for(int i = 0; i < getChildren().size; i++){ for(int i = 0; i < getChildren().size; i++){
Element e = getChildren().get(i); Element e = getChildren().get(i);
@@ -143,10 +175,22 @@ public class LCanvas extends Table{
} }
} }
prefHeight = cy;
}
@Override
public float getPrefWidth(){
return prefWidth;
}
@Override
public float getPrefHeight(){
return prefHeight;
} }
@Override @Override
public void draw(){ public void draw(){
Draw.alpha(parentAlpha);
//draw selection box indicating placement position //draw selection box indicating placement position
if(dragging != null && insertPosition <= seq.size){ if(dragging != null && insertPosition <= seq.size){
@@ -260,9 +304,9 @@ public class LCanvas extends Table{
@Override @Override
public void draw(){ public void draw(){
float pad = 5f; float pad = 5f;
Fill.dropShadow(x + width/2f, y + height/2f, width + pad, height + pad, 10f, 0.9f); Fill.dropShadow(x + width/2f, y + height/2f, width + pad, height + pad, 10f, 0.9f * parentAlpha);
Draw.color(0, 0, 0, 0.3f); Draw.color(0, 0, 0, 0.3f * parentAlpha);
Fill.crect(x, y, width, height); Fill.crect(x, y, width, height);
Draw.reset(); Draw.reset();
@@ -323,24 +367,31 @@ public class LCanvas extends Table{
public void draw(){ public void draw(){
super.draw(); super.draw();
postDraw.add(() -> { postDraw.add(() -> {
Element hover = to.get() == null && selecting ? hovered() : to.get(); Element hover = to.get() == null && selecting ? hovered() : to.get();
float tx = 0, ty = 0; float tx = 0, ty = 0;
boolean draw = false; boolean draw = false;
//capture coordinates for use in lambda //capture coordinates for use in lambda
float rx = x, ry = y; float rx = x + translation.x, ry = y + translation.y;
Element p = parent; Element p = parent;
while(p != null){ while(p != null){
rx += p.x; rx += p.x + p.translation.x;
ry += p.y; ry += p.y + p.translation.y;
p = p.parent; p = p.parent;
} }
if(hover != null){ if(hover != null){
tx = hover.getX(Align.right) + hover.translation.x; tx = hover.getX(Align.right) + hover.translation.x;
ty = hover.getY(Align.right) + hover.translation.y; ty = hover.getY(Align.right) + hover.translation.y;
Element op = hover.parent;
while(op != null){
tx += op.x + op.translation.x;
ty += op.y + op.translation.y;
op = op.parent;
}
draw = true; draw = true;
}else if(selecting){ }else if(selecting){
tx = rx + mx; tx = rx + mx;
+23 -10
View File
@@ -16,7 +16,7 @@ public class LExecutor{
return instructions != null && vars != null && instructions.length > 0; return instructions != null && vars != null && instructions.length > 0;
} }
/** Runs all the instructions at once. Debugging only. */ /** Runs all the instructions at once. Debugging only! */
public void runAll(){ public void runAll(){
counter = 0; counter = 0;
@@ -25,6 +25,19 @@ public class LExecutor{
} }
} }
public void runOnce(){
//reset to start
if(counter >= instructions.length) counter = 0;
if(counter < instructions.length){
instructions[counter++].run(this);
}
}
public void load(Object context, String data){
load(context,LAssembler.assemble(data));
}
public void load(Object context, LAssembler builder){ public void load(Object context, LAssembler builder){
builder.putConst("this", context); builder.putConst("this", context);
@@ -105,15 +118,15 @@ public class LExecutor{
} }
/** Enables/disables a building. */ /** Enables/disables a building. */
public static class ToggleI implements LInstruction{ public static class EnableI implements LInstruction{
public int target, value; public int target, value;
public ToggleI(int target, int value){ public EnableI(int target, int value){
this.target = target; this.target = target;
this.value = value; this.value = value;
} }
ToggleI(){} EnableI(){}
@Override @Override
public void run(LExecutor exec){ public void run(LExecutor exec){
@@ -131,15 +144,15 @@ public class LExecutor{
} }
} }
public static class AssignI implements LInstruction{ public static class SetI implements LInstruction{
public int from, to; public int from, to;
public AssignI(int from, int to){ public SetI(int from, int to){
this.from = from; this.from = from;
this.to = to; this.to = to;
} }
AssignI(){} SetI(){}
@Override @Override
public void run(LExecutor exec){ public void run(LExecutor exec){
@@ -219,17 +232,17 @@ public class LExecutor{
} }
} }
public static class FetchBuildI implements LInstruction{ public static class GetBuildI implements LInstruction{
public int dest; public int dest;
public int x, y; public int x, y;
public FetchBuildI(int dest, int x, int y){ public GetBuildI(int dest, int x, int y){
this.dest = dest; this.dest = dest;
this.x = x; this.x = x;
this.y = y; this.y = y;
} }
FetchBuildI(){} GetBuildI(){}
@Override @Override
public void run(LExecutor exec){ public void run(LExecutor exec){
+8 -1
View File
@@ -2,10 +2,13 @@ package mindustry.logic;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
import arc.util.ArcAnnotate.*; import arc.util.ArcAnnotate.*;
import mindustry.gen.*;
import mindustry.logic.LCanvas.*; import mindustry.logic.LCanvas.*;
import mindustry.logic.LExecutor.*; import mindustry.logic.LExecutor.*;
/** A statement is an intermediate representation of an instruction, to be used in UI. */ /**
* A statement is an intermediate representation of an instruction, to be used mostly in UI.
* Contains all relevant variable information. */
public abstract class LStatement{ public abstract class LStatement{
public transient @Nullable StatementElem elem; public transient @Nullable StatementElem elem;
@@ -13,6 +16,10 @@ public abstract class LStatement{
public abstract LCategory category(); public abstract LCategory category();
public abstract LInstruction build(LAssembler builder); public abstract LInstruction build(LAssembler builder);
public void write(StringBuilder builder){
LogicIO.write(this,builder);
}
public void setupUI(){ public void setupUI(){
} }
+15 -8
View File
@@ -3,13 +3,15 @@ package mindustry.logic;
import arc.graphics.*; import arc.graphics.*;
import arc.scene.ui.*; import arc.scene.ui.*;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
import mindustry.annotations.Annotations.*;
import mindustry.logic.LExecutor.*; import mindustry.logic.LExecutor.*;
import mindustry.logic.LCanvas.*; import mindustry.logic.LCanvas.*;
import mindustry.ui.*; import mindustry.ui.*;
public class LStatements{ public class LStatements{
public static class AssignStatement extends LStatement{ @RegisterStatement("set")
public static class SetStatement extends LStatement{
public String to = "result"; public String to = "result";
public String from = "0"; public String from = "0";
@@ -31,12 +33,12 @@ public class LStatements{
@Override @Override
public LInstruction build(LAssembler builder){ public LInstruction build(LAssembler builder){
//TODO internal consts need to start with ___ and not be assignable to return new SetI(builder.var(from), builder.var(to));
return new LExecutor.AssignI(builder.var(from), builder.var(to));
} }
} }
public static class ToggleStatement extends LStatement{ @RegisterStatement("enable")
public static class EnableStatement extends LStatement{
public String target = "result"; public String target = "result";
public String value = "0"; public String value = "0";
@@ -57,11 +59,12 @@ public class LStatements{
} }
@Override @Override
public LExecutor.LInstruction build(LAssembler builder){ public LInstruction build(LAssembler builder){
return new LExecutor.ToggleI(builder.var(target), builder.var(value)); return new EnableI(builder.var(target), builder.var(value));
} }
} }
@RegisterStatement("op")
public static class OpStatement extends LStatement{ public static class OpStatement extends LStatement{
public BinaryOp op = BinaryOp.add; public BinaryOp op = BinaryOp.add;
public String a = "a", b = "b", dest = "result"; public String a = "a", b = "b", dest = "result";
@@ -98,6 +101,7 @@ public class LStatements{
} }
} }
@RegisterStatement("end")
public static class EndStatement extends LStatement{ public static class EndStatement extends LStatement{
@Override @Override
public void build(Table table){ public void build(Table table){
@@ -115,6 +119,7 @@ public class LStatements{
} }
} }
@RegisterStatement("print")
public static class PrintStatement extends LStatement{ public static class PrintStatement extends LStatement{
public String value = "\"frog\""; public String value = "\"frog\"";
@@ -135,6 +140,7 @@ public class LStatements{
} }
} }
@RegisterStatement("jump")
public static class JumpStatement extends LStatement{ public static class JumpStatement extends LStatement{
public transient StatementElem dest; public transient StatementElem dest;
@@ -176,7 +182,8 @@ public class LStatements{
} }
} }
public static class FetchBuildStatement extends LStatement{ @RegisterStatement("getbuild")
public static class getBuildStatement extends LStatement{
public String x = "0", y = "0", dest = "result"; public String x = "0", y = "0", dest = "result";
@Override @Override
@@ -197,7 +204,7 @@ public class LStatements{
@Override @Override
public LInstruction build(LAssembler builder){ public LInstruction build(LAssembler builder){
return new FetchBuildI(builder.var(dest), builder.var(x), builder.var(y)); return new GetBuildI(builder.var(dest), builder.var(x), builder.var(y));
} }
@Override @Override
@@ -16,20 +16,28 @@ public class LogicProcessor extends Block{
config(String.class, (LogicEntity entity, String code) -> { config(String.class, (LogicEntity entity, String code) -> {
if(code != null){ if(code != null){
entity.code = code; entity.code = code;
entity.executor.load(entity, LAssembler.fromJson(code));
try{
entity.executor.load(entity, code);
}catch(Exception e){
e.printStackTrace();
//handle malformed code and replace it with nothing
entity.executor.load(entity, "");
}
} }
}); });
} }
public class LogicEntity extends Building{ public class LogicEntity extends Building{
/** logic "source code" as list of json statements */ /** logic "source code" as list of json statements */
String code = "[]"; String code = "";
LExecutor executor = new LExecutor(); LExecutor executor = new LExecutor();
@Override @Override
public void updateTile(){ public void updateTile(){
if(executor.initialized()){ if(executor.initialized()){
executor.runAll(); executor.runOnce();
} }
} }
@@ -51,7 +59,14 @@ public class LogicProcessor extends Block{
super.read(read, revision); super.read(read, revision);
code = read.str(); code = read.str();
executor.load(this, LAssembler.fromJson(code)); try{
executor.load(this, code);
}catch(Exception e){
e.printStackTrace();
//handle malformed code and ignore it
executor = new LExecutor();
}
} }
} }
} }