Merge branch Anuken:master into pr-readwrite

This commit is contained in:
WayZer
2024-07-31 11:20:20 +08:00
246 changed files with 10693 additions and 6167 deletions

View File

@@ -7,10 +7,8 @@ import arc.math.*;
import arc.struct.*;
import arc.util.*;
import mindustry.*;
import mindustry.content.*;
import mindustry.ctype.*;
import mindustry.game.*;
import mindustry.logic.LExecutor.*;
import mindustry.type.*;
import mindustry.world.*;
import mindustry.world.blocks.legacy.*;
@@ -27,12 +25,11 @@ public class GlobalVars{
public static final Rand rand = new Rand();
//non-constants that depend on state
private static int varTime, varTick, varSecond, varMinute, varWave, varWaveTime, varMapW, varMapH, varServer, varClient, varClientLocale, varClientUnit, varClientName, varClientTeam, varClientMobile;
private static LVar varTime, varTick, varSecond, varMinute, varWave, varWaveTime, varMapW, varMapH, varServer, varClient, varClientLocale, varClientUnit, varClientName, varClientTeam, varClientMobile;
private ObjectIntMap<String> namesToIds = new ObjectIntMap<>();
private Seq<Var> vars = new Seq<>(Var.class);
private ObjectMap<String, LVar> vars = new ObjectMap<>();
private Seq<VarEntry> varEntries = new Seq<>();
private IntSet privilegedIds = new IntSet();
private ObjectSet<String> privilegedNames = new ObjectSet<>();
private UnlockableContent[][] logicIdToContent;
private int[][] contentIdToLogicId;
@@ -118,13 +115,14 @@ public class GlobalVars{
put("@color" + Strings.capitalize(entry.key), entry.value.toDoubleBits());
}
//used as a special value for any environmental solid block
put("@solid", Blocks.stoneWall);
for(UnitType type : Vars.content.units()){
put("@" + type.name, type);
}
for(Weather weather : Vars.content.weathers()){
put("@" + weather.name, weather);
}
//store sensor constants
for(LAccess sensor : LAccess.all){
put("@" + sensor.name(), sensor);
@@ -168,31 +166,31 @@ public class GlobalVars{
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;
varTime.numval = state.tick / 60.0 * 1000.0;
varTick.numval = state.tick;
//shorthands for seconds/minutes spent in save
vars.items[varSecond].numval = state.tick / 60f;
vars.items[varMinute].numval = state.tick / 60f / 60f;
varSecond.numval = state.tick / 60f;
varMinute.numval = state.tick / 60f / 60f;
//wave state
vars.items[varWave].numval = state.wave;
vars.items[varWaveTime].numval = state.wavetime / 60f;
varWave.numval = state.wave;
varWaveTime.numval = state.wavetime / 60f;
vars.items[varMapW].numval = world.width();
vars.items[varMapH].numval = world.height();
varMapW.numval = world.width();
varMapH.numval = world.height();
//network
vars.items[varServer].numval = (net.server() || !net.active()) ? 1 : 0;
vars.items[varClient].numval = net.client() ? 1 : 0;
varServer.numval = (net.server() || !net.active()) ? 1 : 0;
varClient.numval = net.client() ? 1 : 0;
//client
if(!net.server() && player != null){
vars.items[varClientLocale].objval = player.locale();
vars.items[varClientUnit].objval = player.unit();
vars.items[varClientName].objval = player.name();
vars.items[varClientTeam].numval = player.team().id;
vars.items[varClientMobile].numval = mobile ? 1 : 0;
varClientLocale.objval = player.locale();
varClientUnit.objval = player.unit();
varClientName.objval = player.name();
varClientTeam.numval = player.team().id;
varClientMobile.numval = mobile ? 1 : 0;
}
}
@@ -213,84 +211,81 @@ public class GlobalVars{
}
/**
* @return a constant ID > 0 if there is a constant with this name, otherwise -1.
* Attempt to get privileged variable id from non-privileged logic executor returns null constant id.
* @return a constant variable if there is a constant with this name, otherwise null.
* Attempt to get privileged variable from non-privileged logic executor returns null constant.
*/
public int get(String name){
return namesToIds.get(name, -1);
public LVar get(String name){
return vars.get(name);
}
/**
* @return a constant variable by ID. ID is not bound checked and must be positive.
* Attempt to get privileged variable from non-privileged logic executor returns null constant
* @return a constant variable by name
* Attempt to get privileged variable from non-privileged logic executor returns null constant.
*/
public Var get(int id, boolean privileged){
if(!privileged && privilegedIds.contains(id)) return vars.get(namesToIds.get("null"));
return vars.items[id];
public LVar get(String name, boolean privileged){
if(!privileged && privilegedNames.contains(name)) return vars.get("null");
return vars.get(name);
}
/** Sets a global variable by an ID returned from put(). */
public void set(int id, double value){
get(id, true).numval = value;
/** Sets a global variable by name. */
public void set(String name, double value){
get(name, true).numval = value;
}
/** Adds a constant value by name. */
public int put(String name, Object value, boolean privileged){
public LVar put(String name, Object value, boolean privileged){
return put(name, value, privileged, true);
}
/** Adds a constant value by name. */
public int put(String name, Object value, boolean privileged, boolean hidden){
int existingIdx = namesToIds.get(name, -1);
if(existingIdx != -1){ //don't overwrite existing vars (see #6910)
public LVar put(String name, Object value, boolean privileged, boolean hidden){
LVar existingVar = vars.get(name);
if(existingVar != null){ //don't overwrite existing vars (see #6910)
Log.debug("Failed to add global logic variable '@', as it already exists.", name);
return existingIdx;
return existingVar;
}
Var var = new Var(name);
LVar var = new LVar(name);
var.constant = true;
if(value instanceof Number num){
var.isobj = false;
var.numval = num.doubleValue();
}else{
var.isobj = true;
var.objval = value;
}
int index = vars.size;
namesToIds.put(name, index);
if(privileged) privilegedIds.add(index);
vars.add(var);
vars.put(name, var);
if(privileged) privilegedNames.add(name);
if(!hidden){
varEntries.add(new VarEntry(index, name, "", "", privileged));
varEntries.add(new VarEntry(name, "", "", privileged));
}
return index;
return var;
}
public int put(String name, Object value){
public LVar put(String name, Object value){
return put(name, value, false);
}
public int putEntry(String name, Object value){
public LVar putEntry(String name, Object value){
return put(name, value, false, false);
}
public int putEntry(String name, Object value, boolean privileged){
public LVar putEntry(String name, Object value, boolean privileged){
return put(name, value, privileged, false);
}
public void putEntryOnly(String name){
varEntries.add(new VarEntry(0, name, "", "", false));
varEntries.add(new VarEntry(name, "", "", false));
}
/** An entry that describes a variable for documentation purposes. This is *only* used inside UI for global variables. */
public static class VarEntry{
public int id;
public String name, description, icon;
public boolean privileged;
public VarEntry(int id, String name, String description, String icon, boolean privileged){
this.id = id;
public VarEntry(String name, String description, String icon, boolean privileged){
this.name = name;
this.description = description;
this.icon = icon;

View File

@@ -5,12 +5,13 @@ import arc.graphics.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.util.*;
import mindustry.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.ui.*;
import mindustry.ui.dialogs.*;
import static mindustry.Vars.*;
public class GlobalVarsDialog extends BaseDialog{
public GlobalVarsDialog(){
@@ -28,8 +29,7 @@ public class GlobalVarsDialog extends BaseDialog{
cont.pane(t -> {
t.margin(10f).marginRight(16f);
t.defaults().fillX().fillY();
for(var entry : Vars.logicVars.getEntries()){
for(var entry : logicVars.getEntries()){
if(entry.name.startsWith("section")){
Color color = Pal.accent;
t.add("@lglobal." + entry.name).fillX().center().labelAlign(Align.center).colspan(4).color(color).padTop(4f).padBottom(2f).row();

View File

@@ -17,6 +17,7 @@ public enum LAccess{
powerNetOut,
ammo,
ammoCapacity,
currentAmmoType,
health,
maxHealth,
heat,
@@ -28,6 +29,8 @@ public enum LAccess{
rotation,
x,
y,
velocityX,
velocityY,
shootX,
shootY,
cameraX,
@@ -35,6 +38,7 @@ public enum LAccess{
cameraWidth,
cameraHeight,
size,
solid,
dead,
range,
shooting,
@@ -67,7 +71,7 @@ public enum LAccess{
all = values(),
senseable = Seq.select(all, t -> t.params.length <= 1).toArray(LAccess.class),
controls = Seq.select(all, t -> t.params.length > 0).toArray(LAccess.class),
settable = {x, y, rotation, speed, armor, health, team, flag, totalPower, payloadType};
settable = {x, y, velocityX, velocityY, rotation, speed, armor, health, shield, team, flag, totalPower, payloadType};
LAccess(String... params){
this.params = params;
@@ -78,5 +82,4 @@ public enum LAccess{
this.params = params;
isObj = obj;
}
}

View File

@@ -14,16 +14,15 @@ public class LAssembler{
private static final int invalidNum = Integer.MIN_VALUE;
private int lastVar;
private boolean privileged;
/** Maps names to variable IDs. */
public ObjectMap<String, BVar> vars = new ObjectMap<>();
/** Maps names to variable. */
public OrderedMap<String, LVar> vars = new OrderedMap<>();
/** All instructions to be executed. */
public LInstruction[] instructions;
public LAssembler(){
//instruction counter
putVar("@counter").value = 0;
putVar("@counter");
//currently controlled unit
putConst("@unit", null);
//reference to self
@@ -57,20 +56,17 @@ public class LAssembler{
return new LParser(text, privileged).parse();
}
/** @return a variable ID by name.
/** @return a variable by name.
* This may be a constant variable referring to a number or object. */
public int var(String symbol){
int constId = Vars.logicVars.get(symbol);
if(constId > 0){
//global constants are *negated* and stored separately
return -constId;
}
public LVar var(String symbol){
LVar constVar = Vars.logicVars.get(symbol);
if(constVar != null) return constVar;
symbol = symbol.trim();
//string case
if(!symbol.isEmpty() && symbol.charAt(0) == '\"' && symbol.charAt(symbol.length() - 1) == '\"'){
return putConst("___" + symbol, symbol.substring(1, symbol.length() - 1).replace("\\n", "\n")).id;
return putConst("___" + symbol, symbol.substring(1, symbol.length() - 1).replace("\\n", "\n"));
}
//remove spaces for non-strings
@@ -79,10 +75,10 @@ public class LAssembler{
double value = parseDouble(symbol);
if(value == invalidNum){
return putVar(symbol).id;
return putVar(symbol);
}else{
//this creates a hidden const variable with the specified value
return putConst("___" + value, value).id;
return putConst("___" + value, value);
}
}
@@ -106,48 +102,34 @@ public class LAssembler{
}
/** Adds a constant value by name. */
public BVar putConst(String name, Object value){
BVar var = putVar(name);
public LVar putConst(String name, Object value){
LVar var = putVar(name);
if(value instanceof Number number){
var.isobj = false;
var.numval = number.doubleValue();
var.objval = null;
}else{
var.isobj = true;
var.objval = value;
}
var.constant = true;
var.value = value;
return var;
}
/** Registers a variable name mapping. */
public BVar putVar(String name){
public LVar putVar(String name){
if(vars.containsKey(name)){
return vars.get(name);
}else{
BVar var = new BVar(lastVar++);
LVar var = new LVar(name);
vars.put(name, var);
return var;
}
}
@Nullable
public BVar getVar(String name){
public LVar getVar(String name){
return vars.get(name);
}
/** A variable "builder". */
public static class BVar{
public int id;
public boolean constant;
public Object value;
public BVar(int id){
this.id = id;
}
BVar(){}
@Override
public String toString(){
return "BVar{" +
"id=" + id +
", constant=" + constant +
", value=" + value +
'}';
}
}
}

View File

@@ -164,6 +164,12 @@ public class LCanvas extends Table{
this.statements.layout();
}
public void clearStatements(){
jumps.clear();
statements.clearChildren();
statements.layout();
}
StatementElem checkHovered(){
Element e = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true);
if(e != null){

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,7 @@ public enum LMarkerControl{
stroke("stroke"),
rotation("rotation"),
shape("sides", "fill", "outline"),
arc("start", "end"),
flushText("fetch"),
fontSize("size"),
textHeight("height"),

View File

@@ -16,7 +16,7 @@ import mindustry.logic.LCanvas.*;
import mindustry.logic.LExecutor.*;
import mindustry.ui.*;
import static mindustry.Vars.ui;
import static mindustry.Vars.*;
import static mindustry.logic.LCanvas.*;
/**

View File

@@ -260,6 +260,13 @@ public class LStatements{
}, 2, cell -> cell.size(165, 50)));
}, Styles.logict, () -> {}).size(165, 40).color(s.color).left().padLeft(2);
}
case translate, scale -> {
fields(s, "x", x, v -> x = v);
fields(s, "y", y, v -> y = v);
}
case rotate -> {
fields(s, "degrees", p1, v -> p1 = v);
}
}
}).expand().left();
}
@@ -274,8 +281,8 @@ public class LStatements{
@Override
public LInstruction build(LAssembler builder){
return new DrawI((byte)type.ordinal(), 0, builder.var(x), builder.var(y),
type == GraphicsType.print ? nameToAlign.get(p1, Align.bottomLeft) : builder.var(p1), builder.var(p2), builder.var(p3), builder.var(p4));
return new DrawI((byte)type.ordinal(), builder.var(x), builder.var(y),
type == GraphicsType.print ? new LVar(p1, nameToAlign.get(p1, Align.bottomLeft), true) : builder.var(p1), builder.var(p2), builder.var(p3), builder.var(p4));
}
@Override
@@ -1038,7 +1045,7 @@ public class LStatements{
@Override
public LInstruction build(LAssembler builder){
return new RadarI(target1, target2, target3, sort, LExecutor.varUnit, builder.var(sortOrder), builder.var(output));
return new RadarI(target1, target2, target3, sort, builder.var("@unit"), builder.var(sortOrder), builder.var(output));
}
@Override
@@ -1373,6 +1380,115 @@ public class LStatements{
}
}
@RegisterStatement("weathersense")
public static class WeatherSenseStatement extends LStatement{
public String to = "result";
public String weather = "@rain";
private transient TextField tfield;
@Override
public void build(Table table){
field(table, to, str -> to = str);
table.add(" = weather ");
row(table);
tfield = field(table, weather, str -> weather = str).padRight(0f).get();
table.button(b -> {
b.image(Icon.pencilSmall);
b.clicked(() -> showSelectTable(b, (t, hide) -> {
t.row();
t.table(i -> {
i.left();
int c = 0;
for(Weather w : Vars.content.weathers()){
i.button(w.name, Styles.flatt, () -> {
weather = "@" + w.name;
tfield.setText(weather);
hide.run();
}).height(40f).uniformX().wrapLabel(false).growX();
if(++c % 2 == 0) i.row();
}
}).left();
}));
}, Styles.logict, () -> {}).size(40f).padLeft(-1).color(table.color);
}
@Override
public boolean privileged(){
return true;
}
@Override
public LInstruction build(LAssembler builder){
return new SenseWeatherI(builder.var(weather), builder.var(to));
}
@Override
public LCategory category(){
return LCategory.world;
}
}
@RegisterStatement("weatherset")
public static class WeatherSetStatement extends LStatement{
public String weather = "@rain", state = "true";
private transient TextField tfield;
@Override
public void build(Table table){
table.add(" set weather ");
tfield = field(table, weather, str -> weather = str).padRight(0f).get();
table.button(b -> {
b.image(Icon.pencilSmall);
b.clicked(() -> showSelectTable(b, (t, hide) -> {
t.row();
t.table(i -> {
i.left();
int c = 0;
for(Weather w : Vars.content.weathers()){
i.button(w.name, Styles.flatt, () -> {
weather = "@" + w.name;
tfield.setText(weather);
hide.run();
}).height(40f).uniformX().wrapLabel(false).growX();
if(++c % 2 == 0) i.row();
}
}).left();
}));
}, Styles.logict, () -> {}).size(40f).padLeft(-1).color(table.color);
table.add(" state ");
fields(table, state, str -> state = str);
}
@Override
public boolean privileged(){
return true;
}
@Override
public LInstruction build(LAssembler builder){
return new SetWeatherI(builder.var(weather), builder.var(state));
}
@Override
public LCategory category(){
return LCategory.world;
}
}
@RegisterStatement("spawnwave")
public static class SpawnWaveStatement extends LStatement{
public String x = "10", y = "10", natural = "false";
@@ -1740,11 +1856,17 @@ public class LStatements{
fields(table, index, i -> index = i);
}
if(type == FetchType.buildCount || type == FetchType.build || type == FetchType.unitCount){
if(type == FetchType.buildCount || type == FetchType.build){
row(table);
fields(table, "block", extra, i -> extra = i);
}
if(type == FetchType.unitCount || type == FetchType.unit){
row(table);
fields(table, "unit", extra, i -> extra = i);
}
}
@Override
@@ -1789,6 +1911,42 @@ public class LStatements{
}
}
@RegisterStatement("clientdata")
public static class ClientDataStatement extends LStatement{
public String channel = "\"frog\"", value = "\"bar\"", reliable = "0";
@Override
public void build(Table table){
table.add("send ");
fields(table, value, str -> value = str);
table.add(" on ");
fields(table, channel, str -> channel = str);
table.add(", reliable ");
fields(table, reliable, str -> reliable = str);
}
@Override
public boolean hidden(){
return true;
}
@Override
public boolean privileged(){
return true;
}
@Override
public LInstruction build(LAssembler builder){
if(!state.rules.allowLogicData) return null;
return new ClientDataI(builder.var(channel), builder.var(value), builder.var(reliable));
}
@Override
public LCategory category(){
return LCategory.world;
}
}
@RegisterStatement("getflag")
public static class GetFlagStatement extends LStatement{
public String result = "result", flag = "\"flag\"";

View File

@@ -0,0 +1,108 @@
package mindustry.logic;
import arc.util.*;
import mindustry.game.*;
import mindustry.gen.*;
public class LVar{
public final String name;
public int id;
public boolean isobj, constant;
public Object objval;
public double numval;
//ms timestamp for when this was last synced; used in the sync instruction
public long syncTime;
public LVar(String name){
this(name, -1);
}
public LVar(String name, int id){
this(name, id, false);
}
public LVar(String name, int id, boolean constant){
this.name = name;
this.id = id;
this.constant = constant;
}
public @Nullable Building building(){
return isobj && objval instanceof Building building ? building : null;
}
public @Nullable Object obj(){
return isobj ? objval : null;
}
public @Nullable Team team(){
if(isobj){
return objval instanceof Team t ? t : null;
}else{
int t = (int)numval;
if(t < 0 || t >= Team.all.length) return null;
return Team.all[t];
}
}
public boolean bool(){
return isobj ? objval != null : Math.abs(numval) >= 0.00001;
}
public double num(){
return isobj ? objval != null ? 1 : 0 : invalid(numval) ? 0 : numval;
}
/** Get num value from variable, convert null to NaN to handle it differently in some instructions */
public double numOrNan(){
return isobj ? objval != null ? 1 : Double.NaN : invalid(numval) ? 0 : numval;
}
public float numf(){
return isobj ? objval != null ? 1 : 0 : invalid(numval) ? 0 : (float)numval;
}
/** Get float value from variable, convert null to NaN to handle it differently in some instructions */
public float numfOrNan(){
return isobj ? objval != null ? 1 : Float.NaN : invalid(numval) ? 0 : (float)numval;
}
public int numi(){
return (int)num();
}
public void setbool(boolean value){
setnum(value ? 1 : 0);
}
public void setnum(double value){
if(constant) return;
if(invalid(value)){
objval = null;
isobj = true;
}else{
numval = value;
objval = null;
isobj = false;
}
}
public void setobj(Object value){
if(constant) return;
objval = value;
isobj = true;
}
public void setconst(Object value){
objval = value;
isobj = true;
}
public static boolean invalid(double d){
return Double.isNaN(d) || Double.isInfinite(d);
}
}

View File

@@ -17,6 +17,7 @@ import mindustry.logic.LExecutor.*;
import mindustry.logic.LStatements.*;
import mindustry.ui.*;
import mindustry.ui.dialogs.*;
import mindustry.world.blocks.logic.*;
import static mindustry.Vars.*;
import static mindustry.logic.LCanvas.*;
@@ -52,7 +53,7 @@ public class LogicDialog extends BaseDialog{
add(buttons).growX().name("canvas");
}
public static Color typeColor(Var s, Color color){
public static Color typeColor(LVar s, Color color){
return color.set(
!s.isobj ? Pal.place :
s.objval == null ? Color.darkGray :
@@ -66,7 +67,7 @@ public class LogicDialog extends BaseDialog{
);
}
public static String typeName(Var s){
public static String typeName(LVar s){
return
!s.isobj ? "number" :
s.objval == null ? "null" :
@@ -92,11 +93,29 @@ public class LogicDialog extends BaseDialog{
TextButtonStyle style = Styles.flatt;
t.defaults().size(280f, 60f).left();
if(privileged && executor != null && executor.build != null && !ui.editor.isShown()){
t.button("@editor.worldprocessors.editname", Icon.edit, style, () -> {
ui.showTextInput("", "@editor.name", LogicBlock.maxNameLength, executor.build.tag == null ? "" : executor.build.tag, tag -> {
if(privileged && executor != null && executor.build != null){
executor.build.configure(tag);
//just in case of privilege shenanigans...
executor.build.tag = tag;
}
});
dialog.hide();
}).marginLeft(12f).row();
}
t.button("@clear", Icon.cancel, style, () -> {
ui.showConfirm("@logic.clear.confirm", () -> canvas.clearStatements());
dialog.hide();
}).marginLeft(12f).row();
t.button("@schematic.copy", Icon.copy, style, () -> {
dialog.hide();
Core.app.setClipboardText(canvas.save());
}).marginLeft(12f);
t.row();
}).marginLeft(12f).row();
t.button("@schematic.copy.import", Icon.download, style, () -> {
dialog.hide();
try{

View File

@@ -15,6 +15,7 @@ public enum LogicRule{
lighting,
ambientLight,
solarMultiplier,
dragMultiplier,
ban,
unban,