Functional world processors

This commit is contained in:
Anuken
2022-02-08 12:18:48 -05:00
parent e4dd7bf14b
commit 38c0284bbe
21 changed files with 363 additions and 60 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

View File

@@ -530,3 +530,4 @@
63173=beam-link|block-beam-link-ui 63173=beam-link|block-beam-link-ui
63172=drone-center|block-drone-center-ui 63172=drone-center|block-drone-center-ui
63171=effect-drone|unit-effect-drone-ui 63171=effect-drone|unit-effect-drone-ui
63170=world-processor|block-world-processor-ui

Binary file not shown.

View File

@@ -149,6 +149,7 @@ public class Blocks{
//logic //logic
message, switchBlock, microProcessor, logicProcessor, hyperProcessor, largeLogicDisplay, logicDisplay, memoryCell, memoryBank, message, switchBlock, microProcessor, logicProcessor, hyperProcessor, largeLogicDisplay, logicDisplay, memoryCell, memoryBank,
worldProcessor,
//campaign //campaign
//TODO launch pad on erekir, 5x5, uses nuclear(?) fuel //TODO launch pad on erekir, 5x5, uses nuclear(?) fuel
@@ -3778,6 +3779,16 @@ public class Blocks{
size = 6; size = 6;
}}; }};
worldProcessor = new LogicBlock("world-processor"){{
//currently incomplete, debugOnly for now
requirements(Category.logic, BuildVisibility.debugOnly, with());
instructionsPerTick = 2;
forceDark = true;
privileged = true;
size = 1;
}};
//endregion //endregion
} }
} }

View File

@@ -402,6 +402,8 @@ public class Logic implements ApplicationListener{
} }
Time.update(); Time.update();
constants.update();
//weather is serverside //weather is serverside
if(!net.client() && !state.isEditor()){ if(!net.client() && !state.isEditor()){
updateWeather(); updateWeather();

View File

@@ -530,7 +530,7 @@ public class World{
} }
Tile tile = world.tile(x, y); Tile tile = world.tile(x, y);
if(tile != null && tile.block().solid && tile.block().fillsTile && !tile.block().synthetic()){ if(tile != null && tile.isDarkened()){
dark = Math.max(dark, tile.data); dark = Math.max(dark, tile.data);
} }

View File

@@ -119,6 +119,7 @@ public class Pal{
logicOperations = Color.valueOf("877bad"), logicOperations = Color.valueOf("877bad"),
logicIo = Color.valueOf("a08a8a"), logicIo = Color.valueOf("a08a8a"),
logicUnits = Color.valueOf("c7b59d"), logicUnits = Color.valueOf("c7b59d"),
logicWorld = Color.valueOf("6b84d4"),
berylShot = Color.valueOf("b1dd7e"), berylShot = Color.valueOf("b1dd7e"),
tungstenShot = Color.valueOf("768a9a"), tungstenShot = Color.valueOf("768a9a"),

View File

@@ -15,6 +15,8 @@ import mindustry.world.*;
import java.io.*; import java.io.*;
import static mindustry.Vars.*;
/** Stores global constants for logic processors. */ /** Stores global constants for logic processors. */
public class GlobalConstants{ public class GlobalConstants{
public static final int ctrlProcessor = 1, ctrlPlayer = 2, ctrlFormation = 3; public static final int ctrlProcessor = 1, ctrlPlayer = 2, ctrlFormation = 3;
@@ -22,6 +24,9 @@ public class GlobalConstants{
/** Global random state. */ /** Global random state. */
public static final Rand rand = new Rand(); public static final Rand rand = new Rand();
//non-constants that depend on state
private static int varTime, varTick, varWave, varWaveTime;
private ObjectIntMap<String> namesToIds = new ObjectIntMap<>(); private ObjectIntMap<String> namesToIds = new ObjectIntMap<>();
private Seq<Var> vars = new Seq<>(Var.class); private Seq<Var> vars = new Seq<>(Var.class);
private UnlockableContent[][] logicIdToContent; private UnlockableContent[][] logicIdToContent;
@@ -34,6 +39,12 @@ public class GlobalConstants{
put("true", 1); put("true", 1);
put("null", null); put("null", null);
//time
varTime = put("@time", 0);
varTick = put("@tick", 0);
varWave = put("@waveNumber", 0);
varWaveTime = put("@waveTime", 0);
//special enums //special enums
put("@ctrlProcessor", ctrlProcessor); put("@ctrlProcessor", ctrlProcessor);
@@ -51,9 +62,7 @@ public class GlobalConstants{
} }
for(Block block : Vars.content.blocks()){ for(Block block : Vars.content.blocks()){
if(block.synthetic()){ put("@" + block.name, block);
put("@" + block.name, block);
}
} }
//used as a special value for any environmental solid block //used as a special value for any environmental solid block
@@ -105,6 +114,18 @@ public class GlobalConstants{
} }
} }
/** Updates global time and other state variables. */
public void update(){
//set up time; note that @time is now only updated once every invocation and directly based off of @tick.
//having time be based off of user system time was a very bad idea.
vars.items[varTime].numval = state.tick / 60.0 * 1000.0;
vars.items[varTick].numval = state.tick;
//wave state
vars.items[varWave].numval = state.wave;
vars.items[varWaveTime].numval = state.wavetime / 60f;
}
/** @return a piece of content based on its logic ID. This is not equivalent to content ID. */ /** @return a piece of content based on its logic ID. This is not equivalent to content ID. */
public @Nullable Content lookupContent(ContentType type, int id){ public @Nullable Content lookupContent(ContentType type, int id){
var arr = logicIdToContent[type.ordinal()]; var arr = logicIdToContent[type.ordinal()];
@@ -127,8 +148,13 @@ public class GlobalConstants{
return vars.items[id]; return vars.items[id];
} }
/** Sets a global variable by an ID returned from put(). */
public void set(int id, double value){
get(id).numval = value;
}
/** Adds a constant value by name. */ /** Adds a constant value by name. */
public Var put(String name, Object value){ public int put(String name, Object value){
Var var = new Var(name); Var var = new Var(name);
var.constant = true; var.constant = true;
if(value instanceof Number num){ if(value instanceof Number num){
@@ -141,6 +167,6 @@ public class GlobalConstants{
int index = vars.size; int index = vars.size;
namesToIds.put(name, index); namesToIds.put(name, index);
vars.add(var); vars.add(var);
return var; return index;
} }
} }

View File

@@ -22,20 +22,28 @@ public class LAssembler{
public LAssembler(){ public LAssembler(){
//instruction counter //instruction counter
putVar("@counter").value = 0; putVar("@counter").value = 0;
//timestamp
putConst("@time", 0);
//currently controlled unit //currently controlled unit
putConst("@unit", null); putConst("@unit", null);
//reference to self //reference to self
putConst("@this", null); putConst("@this", null);
//global tick
putConst("@tick", 0);
} }
/** @deprecated use the one with the privileged parameter */
@Deprecated
public static LAssembler assemble(String data){ public static LAssembler assemble(String data){
return assemble(data, false);
}
/** @deprecated use the one with the privileged parameter */
@Deprecated
public static Seq<LStatement> read(String text){
return read(text, false);
}
public static LAssembler assemble(String data, boolean privileged){
LAssembler asm = new LAssembler(); LAssembler asm = new LAssembler();
Seq<LStatement> st = read(data); Seq<LStatement> st = read(data, privileged);
asm.instructions = st.map(l -> l.build(asm)).filter(l -> l != null).toArray(LInstruction.class); asm.instructions = st.map(l -> l.build(asm)).filter(l -> l != null).toArray(LInstruction.class);
return asm; return asm;
@@ -51,8 +59,11 @@ public class LAssembler{
return out.toString(); return out.toString();
} }
public static Seq<LStatement> read(String data){ /** Parses a sequence of statements from a string. */
return LParser.parse(data); public static Seq<LStatement> read(String text, boolean privileged){
//don't waste time parsing null/empty text
if(text == null || text.isEmpty()) return new Seq<>();
return new LParser(text, privileged).parse();
} }
/** @return a variable ID by name. /** @return a variable ID by name.

View File

@@ -27,10 +27,12 @@ public class LCanvas extends Table{
public DragLayout statements; public DragLayout statements;
public ScrollPane pane; public ScrollPane pane;
public Group jumps; public Group jumps;
StatementElem dragging; StatementElem dragging;
StatementElem hovered; StatementElem hovered;
float targetWidth; float targetWidth;
int jumpCount = 0; int jumpCount = 0;
boolean privileged;
Seq<Tooltip> tooltips = new Seq<>(); Seq<Tooltip> tooltips = new Seq<>();
public LCanvas(){ public LCanvas(){
@@ -146,7 +148,7 @@ public class LCanvas extends Table{
public void load(String asm){ public void load(String asm){
jumps.clear(); jumps.clear();
Seq<LStatement> statements = LAssembler.read(asm); Seq<LStatement> statements = LAssembler.read(asm, privileged);
statements.truncate(LExecutor.maxInstructions); statements.truncate(LExecutor.maxInstructions);
this.statements.clearChildren(); this.statements.clearChildren();
for(LStatement st : statements){ for(LStatement st : statements){

View File

@@ -16,6 +16,7 @@ import mindustry.game.Teams.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.type.*; import mindustry.type.*;
import mindustry.world.*; import mindustry.world.*;
import mindustry.world.blocks.environment.*;
import mindustry.world.blocks.logic.*; import mindustry.world.blocks.logic.*;
import mindustry.world.blocks.logic.LogicDisplay.*; import mindustry.world.blocks.logic.LogicDisplay.*;
import mindustry.world.blocks.logic.MemoryBlock.*; import mindustry.world.blocks.logic.MemoryBlock.*;
@@ -31,10 +32,8 @@ public class LExecutor{
//special variables //special variables
public static final int public static final int
varCounter = 0, varCounter = 0,
varTime = 1, varUnit = 1,
varUnit = 2, varThis = 2;
varThis = 3,
varTick = 4;
public static final int public static final int
maxGraphicsBuffer = 256, maxGraphicsBuffer = 256,
@@ -43,6 +42,7 @@ public class LExecutor{
public LInstruction[] instructions = {}; public LInstruction[] instructions = {};
public Var[] vars = {}; public Var[] vars = {};
public Var counter;
public int[] binds; public int[] binds;
public LongSeq graphicsBuffer = new LongSeq(); public LongSeq graphicsBuffer = new LongSeq();
@@ -50,32 +50,24 @@ public class LExecutor{
public Building[] links = {}; public Building[] links = {};
public IntSet linkIds = new IntSet(); public IntSet linkIds = new IntSet();
public Team team = Team.derelict; public Team team = Team.derelict;
public boolean privileged = false;
public boolean initialized(){ public boolean initialized(){
return instructions != null && vars != null && instructions.length > 0; return instructions.length > 0;
} }
/** Runs a single instruction. */ /** Runs a single instruction. */
public void runOnce(){ public void runOnce(){
//set up time; note that @time is now only updated once every invocation and directly based off of @tick.
//having time be based off of user system time was a very bad idea.
vars[varTime].numval = state.tick / 60.0 * 1000.0;
vars[varTick].numval = state.tick;
//reset to start //reset to start
if(vars[varCounter].numval >= instructions.length || vars[varCounter].numval < 0){ if(counter.numval >= instructions.length || counter.numval < 0){
vars[varCounter].numval = 0; counter.numval = 0;
} }
if(vars[varCounter].numval < instructions.length){ if(counter.numval < instructions.length){
instructions[(int)(vars[varCounter].numval++)].run(this); instructions[(int)(counter.numval++)].run(this);
} }
} }
public void load(String data){
load(LAssembler.assemble(data));
}
/** Loads with a specified assembler. Resets all variables. */ /** Loads with a specified assembler. Resets all variables. */
public void load(LAssembler builder){ public void load(LAssembler builder){
vars = new Var[builder.vars.size]; vars = new Var[builder.vars.size];
@@ -95,6 +87,8 @@ public class LExecutor{
dest.objval = var.value; dest.objval = var.value;
} }
}); });
counter = vars[varCounter];
} }
//region utility //region utility
@@ -560,7 +554,7 @@ public class LExecutor{
@Override @Override
public void run(LExecutor exec){ public void run(LExecutor exec){
Object obj = exec.obj(target); Object obj = exec.obj(target);
if(obj instanceof Building b && b.team == exec.team && exec.linkIds.contains(b.id)){ if(obj instanceof Building b && (exec.privileged || (b.team == exec.team && exec.linkIds.contains(b.id)))){
if(type.isObj && exec.var(p1).isobj){ if(type.isObj && exec.var(p1).isobj){
b.control(type, exec.obj(p1), exec.num(p2), exec.num(p3), exec.num(p4)); b.control(type, exec.obj(p1), exec.num(p2), exec.num(p3), exec.num(p4));
}else{ }else{
@@ -921,7 +915,7 @@ public class LExecutor{
//graphics on headless servers are useless. //graphics on headless servers are useless.
if(Vars.headless) return; if(Vars.headless) return;
if(exec.building(target) instanceof LogicDisplayBuild d && d.team == exec.team){ if(exec.building(target) instanceof LogicDisplayBuild d && (d.team == exec.team || exec.privileged)){
if(d.commands.size + exec.graphicsBuffer.size < maxDisplayBuffer){ if(d.commands.size + exec.graphicsBuffer.size < maxDisplayBuffer){
for(int i = 0; i < exec.graphicsBuffer.size; i++){ for(int i = 0; i < exec.graphicsBuffer.size; i++){
d.commands.addLast(exec.graphicsBuffer.items[i]); d.commands.addLast(exec.graphicsBuffer.items[i]);
@@ -1067,6 +1061,7 @@ public class LExecutor{
} }
} }
//TODO lookup color instruction and inverse lookup
public static class LookupI implements LInstruction{ public static class LookupI implements LInstruction{
public int dest; public int dest;
public int from; public int from;
@@ -1087,6 +1082,79 @@ public class LExecutor{
} }
} }
//endregion
//region privileged / world instructions
public static class GetBlockI implements LInstruction{
public int x, y;
public int dest;
public TileLayer layer = TileLayer.block;
public GetBlockI(int x, int y, int dest, TileLayer layer){
this.x = x;
this.y = y;
this.dest = dest;
this.layer = layer;
}
public GetBlockI(){
}
@Override
public void run(LExecutor exec){
Tile tile = world.tile(exec.numi(x), exec.numi(y));
if(tile == null){
exec.setobj(dest, null);
}else{
exec.setobj(dest, switch(layer){
case floor -> tile.floor();
case ore -> tile.overlay();
case block -> tile.block();
case building -> tile.build;
});
}
}
}
public static class SetBlockI implements LInstruction{
public int x, y;
public int block;
public int team, rotation;
public TileLayer layer = TileLayer.block;
public SetBlockI(int x, int y, int block, int team, int rotation, TileLayer layer){
this.x = x;
this.y = y;
this.block = block;
this.team = team;
this.rotation = rotation;
this.layer = layer;
}
public SetBlockI(){
}
@Override
public void run(LExecutor exec){
Tile tile = world.tile(exec.numi(x), exec.numi(y));
if(tile != null && exec.obj(block) instanceof Block b){
//TODO this can be quite laggy...
switch(layer){
case ore -> {
if(b instanceof OverlayFloor o) tile.setOverlay(o);
}
case floor -> {
if(b instanceof Floor f) tile.setFloor(f);
}
case block -> {
Team t = exec.obj(team) instanceof Team steam ? steam : Team.derelict;
tile.setBlock(b, t, Mathf.clamp(exec.numi(rotation), 0, 3));
}
//building case not allowed
}
}
}
}
//endregion //endregion
} }

View File

@@ -19,18 +19,13 @@ public class LParser{
Seq<LStatement> statements = new Seq<>(); Seq<LStatement> statements = new Seq<>();
char[] chars; char[] chars;
int pos, line, tok; int pos, line, tok;
boolean privileged;
LParser(String text){ LParser(String text, boolean privileged){
this.privileged = privileged;
this.chars = text.toCharArray(); this.chars = text.toCharArray();
} }
/** Parses a sequence of statements from a string. */
public static Seq<LStatement> parse(String text){
//don't waste time parsing null/empty text
if(text == null || text.isEmpty()) return new Seq<>();
return new LParser(text).parse();
}
void comment(){ void comment(){
//read until \n or eof //read until \n or eof
while(pos < chars.length && chars[pos++] != '\n'); while(pos < chars.length && chars[pos++] != '\n');
@@ -142,6 +137,11 @@ public class LParser{
st = new InvalidStatement(); st = new InvalidStatement();
} }
//discard misplaced privileged instructions
if(!privileged && st != null && st.privileged()){
st = new InvalidStatement();
}
//store jumps that use labels //store jumps that use labels
if(st instanceof JumpStatement jump && wasJump){ if(st instanceof JumpStatement jump && wasJump){
jumps.add(new JumpIndex(jump, jumpLoc)); jumps.add(new JumpIndex(jump, jumpLoc));

View File

@@ -30,7 +30,8 @@ public abstract class LStatement{
public LStatement copy(){ public LStatement copy(){
StringBuilder build = new StringBuilder(); StringBuilder build = new StringBuilder();
write(build); write(build);
Seq<LStatement> read = LAssembler.read(build.toString()); //assume privileged when copying, because there's no way privileged instructions can appear here anyway, and the instructions get validated on load anyway
Seq<LStatement> read = LAssembler.read(build.toString(), true);
return read.size == 0 ? null : read.first(); return read.size == 0 ? null : read.first();
} }
@@ -38,6 +39,16 @@ public abstract class LStatement{
return false; return false;
} }
/** Privileged instructions are only allowed in world processors. */
public boolean privileged(){
return false;
}
/** If true, this statement is considered useless with privileged processors and is not allowed in them. */
public boolean nonPrivileged(){
return false;
}
//protected methods are only for internal UI layout utilities //protected methods are only for internal UI layout utilities
protected void param(Cell<Label> label){ protected void param(Cell<Label> label){

View File

@@ -1050,4 +1050,108 @@ public class LStatements{
return new UnitLocateI(locate, flag, builder.var(enemy), builder.var(ore), builder.var(outX), builder.var(outY), builder.var(outFound), builder.var(outBuild)); return new UnitLocateI(locate, flag, builder.var(enemy), builder.var(ore), builder.var(outX), builder.var(outY), builder.var(outFound), builder.var(outBuild));
} }
} }
@RegisterStatement("getblock")
public static class GetBlockStatement extends LStatement{
public TileLayer layer = TileLayer.block;
public String result = "result", x = "0", y = "0";
@Override
public void build(Table table){
fields(table, result, str -> result = str);
table.add(" = get ");
row(table);
table.button(b -> {
b.label(() -> layer.name());
b.clicked(() -> showSelect(b, TileLayer.all, layer, o -> layer = o));
}, Styles.logict, () -> {}).size(64f, 40f).pad(4f).color(table.color);
table.add(" at ");
fields(table, x, str -> x = str);
table.add(", ");
fields(table, y, str -> y = str);
}
@Override
public boolean privileged(){
return true;
}
@Override
public Color color(){
return Pal.logicWorld;
}
@Override
public LInstruction build(LAssembler builder){
return new GetBlockI(builder.var(x), builder.var(y), builder.var(result), layer);
}
}
@RegisterStatement("setblock")
public static class SetBlockStatement extends LStatement{
public TileLayer layer = TileLayer.block;
public String block = "@air", x = "0", y = "0", team = "@derelict", rotation = "0";
@Override
public void build(Table table){
rebuild(table);
}
void rebuild(Table table){
table.clearChildren();
table.add("set");
table.button(b -> {
b.label(() -> layer.name());
b.clicked(() -> showSelect(b, TileLayer.settable, layer, o -> {
layer = o;
rebuild(table);
}));
}, Styles.logict, () -> {}).size(64f, 40f).pad(4f).color(table.color);
row(table);
table.add(" at ");
fields(table, x, str -> x = str);
table.add(", ");
fields(table, y, str -> y = str);
row(table);
table.add(" to ");
fields(table, block, str -> block = str);
if(layer == TileLayer.block){
row(table);
table.add("team ");
fields(table, team, str -> team = str);
table.add(" rotation ");
fields(table, rotation, str -> rotation = str);
}
}
@Override
public boolean privileged(){
return true;
}
@Override
public Color color(){
return Pal.logicWorld;
}
@Override
public LInstruction build(LAssembler builder){
return new SetBlockI(builder.var(x), builder.var(y), builder.var(block), builder.var(team), builder.var(rotation), layer);
}
}
} }

View File

@@ -22,6 +22,7 @@ import static mindustry.logic.LCanvas.*;
public class LogicDialog extends BaseDialog{ public class LogicDialog extends BaseDialog{
public LCanvas canvas; public LCanvas canvas;
Cons<String> consumer = s -> {}; Cons<String> consumer = s -> {};
boolean privileged;
@Nullable LExecutor executor; @Nullable LExecutor executor;
public LogicDialog(){ public LogicDialog(){
@@ -156,7 +157,7 @@ public class LogicDialog extends BaseDialog{
int i = 0; int i = 0;
for(Prov<LStatement> prov : LogicIO.allStatements){ for(Prov<LStatement> prov : LogicIO.allStatements){
LStatement example = prov.get(); LStatement example = prov.get();
if(example instanceof InvalidStatement || example.hidden()) continue; if(example instanceof InvalidStatement || example.hidden() || (example.privileged() && !privileged) || (example.nonPrivileged() && privileged)) continue;
TextButtonStyle style = new TextButtonStyle(Styles.cleart); TextButtonStyle style = new TextButtonStyle(Styles.cleart);
style.fontColor = example.color(); style.fontColor = example.color();
@@ -184,10 +185,12 @@ public class LogicDialog extends BaseDialog{
onResize(() -> canvas.rebuild()); onResize(() -> canvas.rebuild());
} }
public void show(String code, LExecutor executor, Cons<String> modified){ public void show(String code, LExecutor executor, boolean privileged, Cons<String> modified){
this.executor = executor; this.executor = executor;
this.privileged = privileged;
canvas.statements.clearChildren(); canvas.statements.clearChildren();
canvas.rebuild(); canvas.rebuild();
canvas.privileged = privileged;
try{ try{
canvas.load(code); canvas.load(code);
}catch(Throwable t){ }catch(Throwable t){

View File

@@ -0,0 +1,10 @@
package mindustry.logic;
public enum TileLayer{
floor,
ore,
block,
building;
public static final TileLayer[] all = values(), settable = {floor, ore, block};
}

View File

@@ -162,6 +162,8 @@ public class Block extends UnlockableContent{
public CacheLayer cacheLayer = CacheLayer.normal; public CacheLayer cacheLayer = CacheLayer.normal;
/** Special flag; if false, floor will be drawn under this block even if it is cached. */ /** Special flag; if false, floor will be drawn under this block even if it is cached. */
public boolean fillsTile = true; public boolean fillsTile = true;
/** If true, this block can be covered by darkness / fog even if synthetic. */
public boolean forceDark = false;
/** whether this block can be replaced in all cases */ /** whether this block can be replaced in all cases */
public boolean alwaysReplace = false; public boolean alwaysReplace = false;
/** if false, this block can never be replaced. */ /** if false, this block can never be replaced. */

View File

@@ -166,7 +166,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{
} }
public boolean isDarkened(){ public boolean isDarkened(){
return block.solid && !block.synthetic() && block.fillsTile; return block.solid && ((!block.synthetic() && block.fillsTile) || block.forceDark);
} }
public Floor floor(){ public Floor floor(){

View File

@@ -61,6 +61,7 @@ public interface Autotiler{
BuildPlan[] directionals = AutotilerHolder.directionals; BuildPlan[] directionals = AutotilerHolder.directionals;
Arrays.fill(directionals, null); Arrays.fill(directionals, null);
//TODO this is O(n^2), very slow, should use quadtree or intmap or something instead
list.each(other -> { list.each(other -> {
if(other.breaking || other == req) return; if(other.breaking || other == req) return;

View File

@@ -1,5 +1,7 @@
package mindustry.world.blocks.logic; package mindustry.world.blocks.logic;
import arc.Graphics.*;
import arc.Graphics.Cursor.*;
import arc.func.*; import arc.func.*;
import arc.math.geom.*; import arc.math.geom.*;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
@@ -32,6 +34,7 @@ public class LogicBlock extends Block{
public int maxInstructionScale = 5; public int maxInstructionScale = 5;
public int instructionsPerTick = 1; public int instructionsPerTick = 1;
public float range = 8 * 10; public float range = 8 * 10;
public boolean privileged;
public LogicBlock(String name){ public LogicBlock(String name){
super(name); super(name);
@@ -44,9 +47,15 @@ public class LogicBlock extends Block{
//universal, no real requirements //universal, no real requirements
envEnabled = Env.any; envEnabled = Env.any;
config(byte[].class, (LogicBuild build, byte[] data) -> build.readCompressed(data, true)); config(byte[].class, (LogicBuild build, byte[] data) -> {
if(!accessible()) return;
build.readCompressed(data, true);
});
config(Integer.class, (LogicBuild entity, Integer pos) -> { config(Integer.class, (LogicBuild entity, Integer pos) -> {
if(!accessible()) return;
//if there is no valid link in the first place, nobody cares //if there is no valid link in the first place, nobody cares
if(!entity.validLink(world.build(pos))) return; if(!entity.validLink(world.build(pos))) return;
var lbuild = world.build(pos); var lbuild = world.build(pos);
@@ -71,6 +80,15 @@ public class LogicBlock extends Block{
}); });
} }
public boolean accessible(){
return !privileged || state.rules.editor;
}
@Override
public boolean canBreak(Tile tile){
return accessible();
}
public static String getLinkName(Block block){ public static String getLinkName(Block block){
String name = block.name; String name = block.name;
if(name.contains("-")){ if(name.contains("-")){
@@ -124,12 +142,16 @@ public class LogicBlock extends Block{
public void setStats(){ public void setStats(){
super.setStats(); super.setStats();
stats.add(Stat.linkRange, range / 8, StatUnit.blocks); if(!privileged){
stats.add(Stat.instructions, instructionsPerTick * 60, StatUnit.perSecond); stats.add(Stat.linkRange, range / 8, StatUnit.blocks);
stats.add(Stat.instructions, instructionsPerTick * 60, StatUnit.perSecond);
}
} }
@Override @Override
public void drawPlace(int x, int y, int rotation, boolean valid){ public void drawPlace(int x, int y, int rotation, boolean valid){
if(privileged) return;
Drawf.circles(x*tilesize + offset, y*tilesize + offset, range); Drawf.circles(x*tilesize + offset, y*tilesize + offset, range);
} }
@@ -203,6 +225,10 @@ public class LogicBlock extends Block{
/** Block of code to run after load. */ /** Block of code to run after load. */
public @Nullable Runnable loadBlock; public @Nullable Runnable loadBlock;
{
executor.privileged = privileged;
}
public void readCompressed(byte[] data, boolean relative){ public void readCompressed(byte[] data, boolean relative){
try(DataInputStream stream = new DataInputStream(new InflaterInputStream(new ByteArrayInputStream(data)))){ try(DataInputStream stream = new DataInputStream(new InflaterInputStream(new ByteArrayInputStream(data)))){
int version = stream.read(); int version = stream.read();
@@ -292,7 +318,7 @@ public class LogicBlock extends Block{
try{ try{
//create assembler to store extra variables //create assembler to store extra variables
LAssembler asm = LAssembler.assemble(str); LAssembler asm = LAssembler.assemble(str, privileged);
//store connections //store connections
for(LogicLink link : links){ for(LogicLink link : links){
@@ -344,11 +370,29 @@ public class LogicBlock extends Block{
executor.load(asm); executor.load(asm);
}catch(Exception e){ }catch(Exception e){
//handle malformed code and replace it with nothing //handle malformed code and replace it with nothing
executor.load(code = ""); executor.load(LAssembler.assemble(code = "", privileged));
} }
} }
} }
//editor-only processors cannot be damaged or destroyed
@Override
public boolean collide(Bullet other){
return !privileged;
}
@Override
public void damage(float damage){
if(!privileged){
super.damage(damage);
}
}
@Override
public Cursor getCursor(){
return !accessible() ? SystemCursor.arrow : super.getCursor();
}
//logic blocks cause write problems when picked up //logic blocks cause write problems when picked up
@Override @Override
public boolean canPickup(){ public boolean canPickup(){
@@ -426,15 +470,13 @@ public class LogicBlock extends Block{
updateCode(code, true, null); updateCode(code, true, null);
} }
if(enabled){ if(enabled && executor.initialized()){
accumulator += edelta() * instructionsPerTick * (consValid() ? 1 : 0); accumulator += edelta() * instructionsPerTick * (consValid() ? 1 : 0);
if(accumulator > maxInstructionScale * instructionsPerTick) accumulator = maxInstructionScale * instructionsPerTick; if(accumulator > maxInstructionScale * instructionsPerTick) accumulator = maxInstructionScale * instructionsPerTick;
for(int i = 0; i < (int)accumulator; i++){ for(int i = 0; i < (int)accumulator; i++){
if(executor.initialized()){ executor.runOnce();
executor.runOnce();
}
accumulator --; accumulator --;
} }
} }
@@ -460,7 +502,9 @@ public class LogicBlock extends Block{
public void drawConfigure(){ public void drawConfigure(){
super.drawConfigure(); super.drawConfigure();
Drawf.circles(x, y, range); if(!privileged){
Drawf.circles(x, y, range);
}
for(LogicLink l : links){ for(LogicLink l : links){
Building build = world.build(l.x, l.y); Building build = world.build(l.x, l.y);
@@ -486,19 +530,25 @@ public class LogicBlock extends Block{
} }
public boolean validLink(Building other){ public boolean validLink(Building other){
return other != null && other.isValid() && other.team == team && other.within(this, range + other.block.size*tilesize/2f) && !(other instanceof ConstructBuild); return other != null && other.isValid() && (privileged || (other.team == team && other.within(this, range + other.block.size*tilesize/2f))) && !(other instanceof ConstructBuild);
} }
@Override @Override
public void buildConfiguration(Table table){ public void buildConfiguration(Table table){
if(!accessible()){
//go away
deselect();
return;
}
table.button(Icon.pencil, Styles.clearTransi, () -> { table.button(Icon.pencil, Styles.clearTransi, () -> {
ui.logic.show(code, executor, code -> configure(compress(code, relativeConnections()))); ui.logic.show(code, executor, privileged, code -> configure(compress(code, relativeConnections())));
}).size(40); }).size(40);
} }
@Override @Override
public boolean onConfigureTileTapped(Building other){ public boolean onConfigureTileTapped(Building other){
if(this == other){ if(this == other || !accessible()){
deselect(); deselect();
return false; return false;
} }

View File

@@ -24,4 +24,4 @@ android.useAndroidX=true
#used for slow jitpack builds; TODO see if this actually works #used for slow jitpack builds; TODO see if this actually works
org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.socketTimeout=100000
org.gradle.internal.http.connectionTimeout=100000 org.gradle.internal.http.connectionTimeout=100000
archash=3ad7f0345b archash=15cbd2710c