Many various logic changes

This commit is contained in:
Anuken
2020-08-10 14:19:24 -04:00
parent b28e7de1db
commit ea8dccdfb2
16 changed files with 384 additions and 182 deletions

View File

@@ -0,0 +1,6 @@
package mindustry.logic;
/** An object that can be controlled with logic. */
public interface Controllable{
void control(LAccess type, double p1, double p2, double p3, double p4);
}

View File

@@ -0,0 +1,35 @@
package mindustry.logic;
import arc.struct.*;
/** Setter/getter enum for logic-controlled objects. */
public enum LAccess{
totalItems,
totalLiquids,
totalPower,
itemCapacity,
liquidCapacity,
powerCapacity,
powerNetStored,
powerNetCapacity,
powerNetIn,
powerNetOut,
health,
heat,
efficiency,
rotation,
//values with parameters are considered controllable
enabled("to"),
shoot("x", "y", "shoot");
public final String[] parameters;
public static final LAccess[] all = values();
public static final LAccess[] senseable = Seq.select(all, t -> t.parameters.length <= 1).toArray(LAccess.class);
public static final LAccess[] controls = Seq.select(all, t -> t.parameters.length > 0).toArray(LAccess.class);
LAccess(String... parameters){
this.parameters = parameters;
}
}

View File

@@ -6,6 +6,7 @@ import arc.util.ArcAnnotate.*;
import mindustry.*;
import mindustry.gen.*;
import mindustry.logic.LExecutor.*;
import mindustry.logic.LStatements.*;
import mindustry.type.*;
/** "Compiles" a sequence of statements into instructions. */
@@ -39,7 +40,7 @@ public class LAssembler{
//store sensor constants
for(LSensor sensor : LSensor.all){
for(LAccess sensor : LAccess.all){
putConst("@" + sensor.name(), sensor);
}
}
@@ -64,22 +65,48 @@ public class LAssembler{
}
public static Seq<LStatement> read(String data){
//empty data check
if(data == null || data.isEmpty()) return new Seq<>();
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));
try{
//yes, I am aware that this can be split with regex, but that's slow and even more incomprehensible
Seq<String> tokens = new Seq<>();
boolean inString = false;
int lastIdx = 0;
for(int i = 0; i < line.length() + 1; i++){
char c = i == line.length() ? ' ' : line.charAt(i);
if(c == '"'){
inString = !inString;
}else if(c == ' ' && !inString){
tokens.add(line.substring(lastIdx, i).replace("\\n", "\n"));
lastIdx = i + 1;
}
}
String[] arr = tokens.toArray(String.class);
LStatement st = LogicIO.read(arr);
if(st != null){
statements.add(st);
}else{
//attempt parsing using custom parser if a match is found - this is for mods
String first = arr[0];
if(customParsers.containsKey(first)){
statements.add(customParsers.get(first).get(arr));
}else{
//unparseable statement
statements.add(new InvalidStatement());
}
}
}catch(Exception parseFailed){
//when parsing fails, add a dummy invalid statement
statements.add(new InvalidStatement());
}
}
return statements;

View File

@@ -16,7 +16,6 @@ import arc.util.*;
import arc.util.ArcAnnotate.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.logic.LStatements.*;
import mindustry.ui.*;
import mindustry.ui.dialogs.*;
@@ -56,12 +55,6 @@ public class LCanvas extends Table{
dialog.addCloseButton();
dialog.show();
}).height(50f).left().width(400f).marginLeft(10f);
add(new PrintStatement());
add(new SetStatement());
add(new JumpStatement());
add(new EnableStatement());
add(new BinaryOpStatement());
}
private void drawGrid(){

View File

@@ -7,8 +7,7 @@ import mindustry.*;
import mindustry.ctype.*;
import mindustry.gen.*;
import mindustry.world.blocks.logic.LogicDisplay.*;
import static mindustry.world.blocks.logic.LogicDisplay.*;
import mindustry.world.blocks.storage.MessageBlock.*;
public class LExecutor{
//special variables
@@ -16,10 +15,16 @@ public class LExecutor{
varCounter = 0,
varTime = 1;
public static final int
maxGraphicsBuffer = 512,
maxTextBuffer = 256;
public double[] memory = {};
public LInstruction[] instructions = {};
public Var[] vars = {};
public LongSeq graphicsBuffer = new LongSeq();
public StringBuilder textBuffer = new StringBuilder();
public boolean initialized(){
return instructions != null && vars != null && instructions.length > 0;
@@ -125,21 +130,30 @@ public class LExecutor{
void run(LExecutor exec);
}
/** Enables/disables a building. */
public static class EnableI implements LInstruction{
public int target, value;
/** Controls a building's state. */
public static class ControlI implements LInstruction{
public int target;
public LAccess type = LAccess.enabled;
public int p1, p2, p3, p4;
public EnableI(int target, int value){
public ControlI(LAccess type, int target, int p1, int p2, int p3, int p4){
this.type = type;
this.target = target;
this.value = value;
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
this.p4 = p4;
}
EnableI(){}
ControlI(){}
@Override
public void run(LExecutor exec){
Building b = exec.building(target);
if(b != null) b.enabled = exec.bool(value);
Object obj = exec.obj(target);
if(obj instanceof Controllable){
Controllable cont = (Controllable)obj;
cont.control(type, exec.num(p1), exec.num(p2), exec.num(p3), exec.num(p4));
}
}
}
@@ -205,8 +219,8 @@ public class LExecutor{
if(target instanceof Senseable){
if(sense instanceof Content){
output = ((Senseable)target).sense(((Content)sense));
}else if(sense instanceof LSensor){
output = ((Senseable)target).sense(((LSensor)sense));
}else if(sense instanceof LAccess){
output = ((Senseable)target).sense(((LAccess)sense));
}
}
@@ -288,12 +302,17 @@ public class LExecutor{
}
}
public static class DisplayI implements LInstruction{
public static class NoopI implements LInstruction{
@Override
public void run(LExecutor exec){}
}
public static class DrawI implements LInstruction{
public byte type;
public int target;
public int x, y, p1, p2, p3;
public DisplayI(byte type, int target, int x, int y, int p1, int p2, int p3){
public DrawI(byte type, int target, int x, int y, int p1, int p2, int p3){
this.type = type;
this.target = target;
this.x = x;
@@ -303,7 +322,7 @@ public class LExecutor{
this.p3 = p3;
}
public DisplayI(){
public DrawI(){
}
@Override
@@ -311,63 +330,91 @@ public class LExecutor{
//graphics on headless servers are useless.
if(Vars.headless) return;
if(type == commandFlush){
Building build = exec.building(target);
if(build instanceof LogicDisplayEntity){
//flush is a special command
LogicDisplayEntity d = (LogicDisplayEntity)build;
for(int i = 0; i < exec.graphicsBuffer.size; i++){
d.commands.addLast(exec.graphicsBuffer.items[i]);
}
exec.graphicsBuffer.clear();
}
//add graphics calls, cap graphics buffer size
if(exec.graphicsBuffer.size < maxGraphicsBuffer){
exec.graphicsBuffer.add(DisplayCmd.get(type, exec.numi(x), exec.numi(y), exec.numi(p1), exec.numi(p2), exec.numi(p3)));
}
}
}
}else{
//add graphics calls, cap graphics buffer size
if(exec.graphicsBuffer.size < 1024){
exec.graphicsBuffer.add(DisplayCmd.get(type, exec.numi(x), exec.numi(y), exec.numi(p1), exec.numi(p2), exec.numi(p3)));
public static class DrawFlushI implements LInstruction{
public int target;
public DrawFlushI(int target){
this.target = target;
}
public DrawFlushI(){
}
@Override
public void run(LExecutor exec){
//graphics on headless servers are useless.
if(Vars.headless) return;
Building build = exec.building(target);
if(build instanceof LogicDisplayEntity){
LogicDisplayEntity d = (LogicDisplayEntity)build;
for(int i = 0; i < exec.graphicsBuffer.size; i++){
d.commands.addLast(exec.graphicsBuffer.items[i]);
}
exec.graphicsBuffer.clear();
}
}
}
public static class PrintI implements LInstruction{
private static final StringBuilder out = new StringBuilder();
public int value;
public int value, target;
public PrintI(int value, int target){
public PrintI(int value){
this.value = value;
this.target = target;
}
PrintI(){}
@Override
public void run(LExecutor exec){
Building b = exec.building(target);
if(b == null) return;
if(exec.textBuffer.length() >= maxTextBuffer) return;
//this should avoid any garbage allocation
Var v = exec.vars[value];
if(v.isobj && value != 0){
if(v.objval instanceof String){
b.handleString(v.objval);
}else if(v.objval == null){
b.handleString("null");
}else{
b.handleString("[object]");
}
String strValue = v.objval instanceof String ? (String)v.objval : v.objval == null ? "null" : "[object]";
exec.textBuffer.append(strValue);
}else{
out.setLength(0);
//display integer version when possible
if(Math.abs(v.numval - (long)v.numval) < 0.000001){
out.append((long)v.numval);
exec.textBuffer.append((long)v.numval);
}else{
out.append(v.numval);
exec.textBuffer.append(v.numval);
}
b.handleString(out);
}
}
}
public static class PrintFlushI implements LInstruction{
public int target;
public PrintFlushI(int target){
this.target = target;
}
public PrintFlushI(){
}
@Override
public void run(LExecutor exec){
Building build = exec.building(target);
if(build instanceof MessageBlockEntity){
MessageBlockEntity d = (MessageBlockEntity)build;
d.message.setLength(0);
d.message.append(exec.textBuffer, 0, Math.min(exec.textBuffer.length(), maxTextBuffer));
exec.textBuffer.setLength(0);
}
}
}

View File

@@ -1,19 +0,0 @@
package mindustry.logic;
public enum LSensor{
totalItems,
totalLiquids,
totalPower,
itemCapacity,
liquidCapacity,
powerCapacity,
powerNetStored,
powerNetCapacity,
powerNetIn,
powerNetOut,
health,
heat,
efficiency;
public static final LSensor[] all = values();
}

View File

@@ -7,6 +7,7 @@ import arc.scene.*;
import arc.scene.actions.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
import arc.util.ArcAnnotate.*;
import mindustry.gen.*;
@@ -27,9 +28,8 @@ public abstract class LStatement{
public LStatement copy(){
StringBuilder build = new StringBuilder();
write(build);
String[] split = build.toString().split(" ");
LStatement result = LogicIO.read(split);
return result == null && LAssembler.customParsers.containsKey(split[0]) ? LAssembler.customParsers.get(split[0]).get(split) : result;
Seq<LStatement> read = LAssembler.read(build.toString());
return read.size == 0 ? null : read.first();
}
//protected methods are only for internal UI layout utilities

View File

@@ -37,6 +37,24 @@ public class LStatements{
}
}
@RegisterStatement("noop")
public static class InvalidStatement extends LStatement{
@Override
public void build(Table table){
}
@Override
public LCategory category(){
return LCategory.operations;
}
@Override
public LInstruction build(LAssembler builder){
return new NoopI();
}
}
@RegisterStatement("write")
public static class WriteStatement extends LStatement{
public String to = "0";
@@ -90,7 +108,7 @@ public class LStatements{
@RegisterStatement("draw")
public static class DrawStatement extends LStatement{
public CommandType type = CommandType.line;
public GraphicsType type = GraphicsType.line;
public String x = "0", y = "0", p1 = "0", p2 = "0", p3 = "0";
@Override
@@ -105,13 +123,13 @@ public class LStatements{
table.button(b -> {
b.label(() -> type.name());
b.clicked(() -> showSelect(b, CommandType.allNormal, type, t -> {
b.clicked(() -> showSelect(b, GraphicsType.all, type, t -> {
type = t;
rebuild(table);
}, 2, cell -> cell.size(100, 50)));
}, Styles.logict, () -> {}).size(90, 40).color(table.color).left().padLeft(2);
if(type != CommandType.stroke){
if(type != GraphicsType.stroke){
table.row();
}
@@ -158,7 +176,6 @@ public class LStatements{
}
}).expand().left();
}
@Override
@@ -168,13 +185,13 @@ public class LStatements{
@Override
public LInstruction build(LAssembler builder){
return new DisplayI((byte)type.ordinal(), 0, builder.var(x), builder.var(y), builder.var(p1), builder.var(p2), builder.var(p3));
return new DrawI((byte)type.ordinal(), 0, builder.var(x), builder.var(y), builder.var(p1), builder.var(p2), builder.var(p3));
}
}
@RegisterStatement("flush")
public static class FlushStatement extends LStatement{
public String target = "display";
@RegisterStatement("drawflush")
public static class DrawFlushStatement extends LStatement{
public String target = "@0";
@Override
public void build(Table table){
@@ -189,7 +206,102 @@ public class LStatements{
@Override
public LInstruction build(LAssembler builder){
return new DisplayI(commandFlush, builder.var(target), 0, 0, 0, 0, 0);
return new DrawFlushI(builder.var(target));
}
}
@RegisterStatement("print")
public static class PrintStatement extends LStatement{
public String value = "\"frog\"";
@Override
public void build(Table table){
field(table, value, str -> value = str).width(0f).growX().padRight(3);
}
@Override
public LInstruction build(LAssembler builder){
return new PrintI(builder.var(value));
}
@Override
public LCategory category(){
return LCategory.control;
}
}
@RegisterStatement("printflush")
public static class PrintFlushStatement extends LStatement{
public String target = "@0";
@Override
public void build(Table table){
table.add(" to ");
field(table, target, str -> target = str);
}
@Override
public LCategory category(){
return LCategory.blocks;
}
@Override
public LInstruction build(LAssembler builder){
return new PrintFlushI(builder.var(target));
}
}
@RegisterStatement("control")
public static class ControlStatement extends LStatement{
public LAccess type = LAccess.enabled;
public String target = "@0", p1 = "0", p2 = "0", p3 = "0", p4 = "0";
@Override
public void build(Table table){
rebuild(table);
}
void rebuild(Table table){
table.clearChildren();
table.left();
table.add(" set ");
table.button(b -> {
b.label(() -> type.name());
b.clicked(() -> showSelect(b, LAccess.controls, type, t -> {
type = t;
rebuild(table);
}, 2, cell -> cell.size(100, 50)));
}, Styles.logict, () -> {
}).size(90, 40).color(table.color).left().padLeft(2);
table.add(" of ");
field(table, target, v -> target = v);
table.row();
//Q: why don't you just use arrays for this?
//A: arrays aren't as easy to serialize so the code generator doesn't handle them
int c = 0;
for(int i = 0; i < type.parameters.length; i++){
fields(table, type.parameters[i], i == 0 ? p1 : i == 1 ? p2 : i == 2 ? p3 : p4, i == 0 ? v -> p1 = v : i == 1 ? v -> p2 = v : i == 2 ? v -> p3 = v : v -> p4 = v);
if(++c % 2 == 0) table.row();
}
}
@Override
public LCategory category(){
return LCategory.blocks;
}
@Override
public LInstruction build(LAssembler builder){
return new ControlI(type, builder.var(target), builder.var(p1), builder.var(p2), builder.var(p3), builder.var(p4));
}
}
@@ -246,7 +358,7 @@ public class LStatements{
}),
//sensors
new Table(i -> {
for(LSensor sensor : LSensor.all){
for(LAccess sensor : LAccess.senseable){
i.button(sensor.name(), Styles.cleart, () -> {
stype("@" + sensor.name());
hide.run();
@@ -321,31 +433,6 @@ public class LStatements{
}
}
@RegisterStatement("enable")
public static class EnableStatement extends LStatement{
public String target = "result";
public String value = "0";
@Override
public void build(Table table){
field(table, target, str -> target = str);
table.add(" -> ");
field(table, value, str -> value = str);
}
@Override
public LCategory category(){
return LCategory.blocks;
}
@Override
public LInstruction build(LAssembler builder){
return new EnableI(builder.var(target), builder.var(value));
}
}
@RegisterStatement("bop")
public static class BinaryOpStatement extends LStatement{
public BinaryOp op = BinaryOp.add;
@@ -428,31 +515,6 @@ public class LStatements{
}
}
@RegisterStatement("print")
public static class PrintStatement extends LStatement{
public String value = "\"frog\"";
public String target = "result";
@Override
public void build(Table table){
field(table, value, str -> value = str);
table.add(" to ");
field(table, target, str -> target = str);
}
@Override
public LInstruction build(LAssembler builder){
return new PrintI(builder.var(value), builder.var(target));
}
@Override
public LCategory category(){
return LCategory.control;
}
}
@RegisterStatement("jump")
public static class JumpStatement extends LStatement{
public transient StatementElem dest;

View File

@@ -3,6 +3,6 @@ package mindustry.logic;
import mindustry.ctype.*;
public interface Senseable{
double sense(LSensor sensor);
double sense(LAccess sensor);
double sense(Content content);
}