diff --git a/changelog b/changelog index 0aa88b91f..5dab975c1 100644 --- a/changelog +++ b/changelog @@ -16,6 +16,9 @@ * Successor to the BAT9k * Larger footprint, way more storage space * Needs to be placed on a proper foundation or else it'll sink into the ground +* AUTOCAL Automatic Calculator + * Highly advanced programmable RoR device + * Can send and receive signals and do calculations with the same system as NTM's calculator ## Changed * RoR graphs can now have their min and max bounds set to a fixed number diff --git a/src/main/java/com/hbm/inventory/gui/GUIScreenRadioAUTOCAL.java b/src/main/java/com/hbm/inventory/gui/GUIScreenRadioAUTOCAL.java index 7314019ca..0fc9678d3 100644 --- a/src/main/java/com/hbm/inventory/gui/GUIScreenRadioAUTOCAL.java +++ b/src/main/java/com/hbm/inventory/gui/GUIScreenRadioAUTOCAL.java @@ -1,7 +1,11 @@ package com.hbm.inventory.gui; +import java.awt.Desktop; import java.io.File; import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.Arrays; import org.lwjgl.opengl.GL11; @@ -59,11 +63,6 @@ public class GUIScreenRadioAUTOCAL extends GuiScreen { if(checkClick(x, y, 28, 36, 18, 18)) { data = new NBTTagCompound(); data.setBoolean("ignore", true); } if(checkClick(x, y, 48, 36, 18, 18)) { data = new NBTTagCompound(); data.setBoolean("auto", true); } - if(data != null) { - mc.getSoundHandler().playSound(PositionedSoundRecord.func_147674_a(new ResourceLocation("gui.button.press"), 1.0F)); - PacketDispatcher.wrapper.sendToServer(new NBTControlPacket(data, autocal.xCoord, autocal.yCoord, autocal.zCoord)); - } - // open folder and generate new script file if(checkClick(x, y, 104, 36, 18, 18)) { try { @@ -74,8 +73,38 @@ public class GUIScreenRadioAUTOCAL extends GuiScreen { script.setExecutable(false); Class oclass = Class.forName("java.awt.Desktop"); Object object = oclass.getMethod("getDesktop", new Class[0]).invoke((Object) null, new Object[0]); - oclass.getMethod("browse", new Class[] { URI.class }).invoke(object, new Object[] { uploadFolder.toURI() }); - } catch(Throwable ex) { MainRegistry.logger.error("Couldn\'t open link", ex); } + oclass.getMethod("browse", new Class[] { URI.class }).invoke(object, new Object[] { script.toURI() }); + //oclass.getMethod("edit", File.class).invoke(object, new Object[] { script }); + } catch(Throwable ex) { MainRegistry.logger.error("Couldn't open link", ex); } + } + + if(checkClick(x, y, 84, 36, 18, 18)) { + try { + File uploadFolder = new File(MainRegistry.configDir.getParentFile(), "hbmComputerUpload"); + File script = new File(uploadFolder, "script.txt"); + if(!uploadFolder.exists()) uploadFolder.mkdir(); + if(!script.exists()) { + script.createNewFile(); + script.setExecutable(false); + return; + } + /*FileReader reader = new FileReader(script); + BufferedReader buffer = new BufferedReader(reader); + String[] lines = buffer.lines().toArray(String[]::new); + buffer.close(); + // this is going to blow the fuck up once we hit the max packet size, but let's ignore that for now + data = new NBTTagCompound(); + StringBuilder builder = new StringBuilder(); + for(int l = 0; l < lines.length; l++) { + builder.append(lines[i]); + if(l < lines.length - 1) builder.append("\n"); // yeah why the fuck not + } + data.setString("payload", builder.toString());*/ + byte[] bytes = Files.readAllBytes(Paths.get(script.toURI())); + data = new NBTTagCompound(); + data.setString("payload", new String(bytes, StandardCharsets.UTF_8)); + + } catch(Throwable ex) { } } // this thing can both upload and download files so let's be careful about this @@ -87,6 +116,11 @@ public class GUIScreenRadioAUTOCAL extends GuiScreen { // but we can add some extra crap that would either halt common scripting langs entirely or at least disrupt them into not functioning // option 3: enforce validation so only MS-ES1 script can be received by the client. this means that info such as comments or incorrectly written commands // are lost, however this is the safest way because it becomes impossible to send malicious code, but it also interferes with regular user operation more + + if(data != null) { + mc.getSoundHandler().playSound(PositionedSoundRecord.func_147674_a(new ResourceLocation("gui.button.press"), 1.0F)); + PacketDispatcher.wrapper.sendToServer(new NBTControlPacket(data, autocal.xCoord, autocal.yCoord, autocal.zCoord)); + } } protected boolean checkClick(int x, int y, int left, int top, int sizeX, int sizeY) { @@ -100,8 +134,8 @@ public class GUIScreenRadioAUTOCAL extends GuiScreen { if(checkClick(x, y, 48, 36, 18, 18)) this.func_146283_a(Arrays.asList(new String[] {EnumChatFormatting.RED + "Automatic Reboot", "Restarts the computer automatically when", "the program stops due to an error", "or after finishing."}), x, y); if(checkClick(x, y, 84, 36, 18, 18)) this.func_146283_a(Arrays.asList(new String[] {EnumChatFormatting.BLUE + "Upload Program"}), x, y); - if(checkClick(x, y, 104, 36, 18, 18)) this.func_146283_a(Arrays.asList(new String[] {EnumChatFormatting.BLUE + "Open Program Folder"}), x, y); - if(checkClick(x, y, 124, 36, 18, 18)) this.func_146283_a(Arrays.asList(new String[] {EnumChatFormatting.BLUE + "Download Program"}), x, y); + if(checkClick(x, y, 104, 36, 18, 18)) this.func_146283_a(Arrays.asList(new String[] {EnumChatFormatting.BLUE + "Open Program File"}), x, y); + if(checkClick(x, y, 124, 36, 18, 18)) this.func_146283_a(Arrays.asList(new String[] {EnumChatFormatting.BLUE + "Download Program", EnumChatFormatting.RED + "Currently unsupported!"}), x, y); } private void drawGuiContainerBackgroundLayer(float f, int mouseX, int mouseY) { @@ -110,8 +144,8 @@ public class GUIScreenRadioAUTOCAL extends GuiScreen { drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize); if(autocal.isOn) drawTexturedModalRect(guiLeft + 8, guiTop + 36, 150, 0, 18, 18); - if(autocal.ignoreError) drawTexturedModalRect(guiLeft + 28, guiTop + 36, 150, 18, 18, 18); - if(autocal.autoReboot) drawTexturedModalRect(guiLeft + 48, guiTop + 36, 150, 36, 18, 18); + if(!autocal.ignoreError) drawTexturedModalRect(guiLeft + 28, guiTop + 36, 150, 18, 18, 18); + if(!autocal.autoReboot) drawTexturedModalRect(guiLeft + 48, guiTop + 36, 150, 36, 18, 18); } @Override diff --git a/src/main/java/com/hbm/module/IParse.java b/src/main/java/com/hbm/module/IParse.java index 73bd8e0c4..df96547ee 100644 --- a/src/main/java/com/hbm/module/IParse.java +++ b/src/main/java/com/hbm/module/IParse.java @@ -8,27 +8,43 @@ import net.minecraft.world.World; public interface IParse { public EnumStatementReturn eval(ParseContext ctx, String line); + public void generateJumpPoints(ParseContext ctx, String line, int index); public static class ParseContext { public World world; - public NBTTagCompound variables; - public HashMap jmp; + public NBTTagCompound variables = new NBTTagCompound(); + public HashMap jmp = new HashMap(); - public Object buffer; - public int clockSpeed; + public String buffer = ""; + public int clockSpeed = 1; + public int current = 0; - public ParseContext(World world, NBTTagCompound variables, HashMap jmp) { + public ParseContext(World world) { this.world = world; - this.variables = variables; - this.jmp = jmp; + } + + public void turnOff() { + this.clockSpeed = 1; + this.current = 0; + this.buffer = ""; + if(!this.variables.hasNoTags()) this.variables = new NBTTagCompound(); } } public static enum EnumStatementReturn { + /** The command executed correctly (more or less) */ OK, + /** The command hasn't been recognized */ UNRECOGNIZED_COMMAND, + /** The expected parameters aren't present, or the parameters couldn't be parsed (i.e. using an undefined jump point) */ PARAMETER_ERROR, + /** Requests the AUTOCAL unit to end the tick, regardless of how many clock cycles are left */ END_TICK, + /** Requests an AUTOCAL shutdown */ + SHUTDOWN, + /** Skips the instruction, doesn't use up a clock cycle */ + SKIP, + /** General undefined behavior */ UNDEFINED } } diff --git a/src/main/java/com/hbm/module/ParseMSES1.java b/src/main/java/com/hbm/module/ParseMSES1.java index cacc1ffe6..3efc506ef 100644 --- a/src/main/java/com/hbm/module/ParseMSES1.java +++ b/src/main/java/com/hbm/module/ParseMSES1.java @@ -1,9 +1,271 @@ package com.hbm.module; +import java.util.Locale; + +import com.hbm.tileentity.network.RTTYSystem; +import com.hbm.tileentity.network.RTTYSystem.RTTYChannel; +import com.hbm.tileentity.network.TileEntityRadioAUTOCAL; +import com.hbm.util.Calculator; + public class ParseMSES1 implements IParse { @Override public EnumStatementReturn eval(ParseContext ctx, String line) { - return EnumStatementReturn.OK; + String lower = line.toLowerCase(Locale.US); + + // jump point destination, skip + if(lower.startsWith("dest ") || lower.startsWith("# ")) { + return EnumStatementReturn.SKIP; + } + + // no operation, still eats up a clock cycle + if(lower.equals("nop")) { + return EnumStatementReturn.OK; + } + + // sets the desired clock speed, then skips the operation + if(lower.startsWith("clockspeed ")) { + if(line.length() <= 11) return EnumStatementReturn.PARAMETER_ERROR; + try { + int speed = Integer.parseInt(line.substring(11)); + if(speed < 1 || speed > TileEntityRadioAUTOCAL.MAX_CLOCK_SPEED) return EnumStatementReturn.PARAMETER_ERROR; + ctx.clockSpeed = speed; + } catch(Throwable ex) { return EnumStatementReturn.PARAMETER_ERROR; } + return EnumStatementReturn.SKIP; + } + + // sets the script index to the jump point + if(lower.startsWith("jmp ")) { + if(line.length() <= 4) return EnumStatementReturn.PARAMETER_ERROR; + String jmpKey = line.substring(4); + if(ctx.jmp.containsKey(jmpKey)) { + ctx.current = ctx.jmp.get(jmpKey); + return EnumStatementReturn.OK; + } + return EnumStatementReturn.PARAMETER_ERROR; + } + + // sets the script index to the jump point, if the buffer is the string 'true' + if(lower.startsWith("jmpif ")) { + if(line.length() <= 6) return EnumStatementReturn.PARAMETER_ERROR; + if(!ctx.buffer.equals("true")) return EnumStatementReturn.OK; + String jmpKey = line.substring(6); + if(ctx.jmp.containsKey(jmpKey)) { + ctx.current = ctx.jmp.get(jmpKey); + return EnumStatementReturn.OK; + } + return EnumStatementReturn.PARAMETER_ERROR; + } + + // sets the script index to the jump point, if the buffer is the NOT 'true' + if(lower.startsWith("jmpnot ")) { + if(line.length() <= 7) return EnumStatementReturn.PARAMETER_ERROR; + if(ctx.buffer.equals("true")) return EnumStatementReturn.OK; + String jmpKey = line.substring(7); + if(ctx.jmp.containsKey(jmpKey)) { + ctx.current = ctx.jmp.get(jmpKey); + return EnumStatementReturn.OK; + } + return EnumStatementReturn.PARAMETER_ERROR; + } + + // ends the tick regardless of remaining clock cycles + if(lower.equals("endtick")) { + return EnumStatementReturn.END_TICK; + } + + // requests unit to shut down + if(lower.equals("shutdown")) { + return EnumStatementReturn.SHUTDOWN; + } + + // loads the requested variable into the buffer + if(lower.startsWith("load ")) { + if(line.length() <= 5) return EnumStatementReturn.PARAMETER_ERROR; + ctx.buffer = ctx.variables.getString(line.substring(5)); + return EnumStatementReturn.OK; + } + + // saves the buffer with the specified name + if(lower.startsWith("save ")) { + if(line.length() <= 5 || ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR; + ctx.variables.setString(line.substring(5), ctx.buffer); + return EnumStatementReturn.OK; + } + + // stores the specified value in the buffer + if(lower.startsWith("buffer ")) { + if(line.length() <= 7) return EnumStatementReturn.PARAMETER_ERROR; + ctx.buffer = line.substring(7); + return EnumStatementReturn.OK; + } + + // runs the calculation, allows string substitution, saves result to buffer + if(lower.startsWith("eval ")) { + if(line.length() <= 5) return EnumStatementReturn.PARAMETER_ERROR; + String statement = substitute(ctx, line.substring(5)); + try { + double result = Calculator.evaluateExpression(statement); + ctx.buffer = "" + result; + } catch(Throwable ex) { return EnumStatementReturn.PARAMETER_ERROR; } + return EnumStatementReturn.OK; + } + + // runs the calculation, allows string substitution, rounds, saves result to buffer, + if(lower.startsWith("evalr ")) { + if(line.length() <= 6) return EnumStatementReturn.PARAMETER_ERROR; + String statement = substitute(ctx, line.substring(6)); + try { + double result = Calculator.evaluateExpression(statement); + ctx.buffer = "" + (int) Math.round(result); + } catch(Throwable ex) { return EnumStatementReturn.PARAMETER_ERROR; } + return EnumStatementReturn.OK; + } + + // runs the calculation from the buffer, allows string substitution, saves result to buffer + if(lower.equals("evalr")) { + if(ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR; + String statement = substitute(ctx, ctx.buffer); + try { + double result = Calculator.evaluateExpression(statement); + ctx.buffer = "" + (int) Math.round(result); + } catch(Throwable ex) { return EnumStatementReturn.PARAMETER_ERROR; } + return EnumStatementReturn.OK; + } + + // rounds the buffer down to the nearest integer + if(lower.equals("rounddown") || lower.equals("floor")) { + if(ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR; + try { + double d = Double.parseDouble(ctx.buffer); + ctx.buffer = "" + (int) Math.floor(d); + } catch(Exception ex) { return EnumStatementReturn.PARAMETER_ERROR; } + return EnumStatementReturn.OK; + } + + // rounds the buffer up to the nearest integer + if(lower.equals("roundup") || lower.equals("ceil")) { + if(ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR; + try { + double d = Double.parseDouble(ctx.buffer); + ctx.buffer = "" + (int) Math.ceil(d); + } catch(Exception ex) { return EnumStatementReturn.PARAMETER_ERROR; } + return EnumStatementReturn.OK; + } + + // rounds the buffer to the nearest integer (.5 cutoff rule) + if(lower.equals("round") || lower.equals("nearest")) { + if(ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR; + try { + double d = Double.parseDouble(ctx.buffer); + ctx.buffer = "" + (int) Math.round(d); + } catch(Exception ex) { return EnumStatementReturn.PARAMETER_ERROR; } + return EnumStatementReturn.OK; + } + + // concatenate, same as buffer but evaluates $var$ substitutions + if(lower.startsWith("concat ")) { + if(line.length() <= 7 || ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR; + String concat = substitute(ctx, line.substring(5)); + ctx.buffer = concat; + return EnumStatementReturn.OK; + } + + // compares the buffer with a value, allows substitutions + if(lower.startsWith("eq ")) { + if(line.length() <= 3 || ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR; + ctx.buffer = ctx.buffer.equals(substitute(ctx, line.substring(3))) ? "true" : "false"; + return EnumStatementReturn.OK; + } + + // greater than buffer + if(lower.startsWith("gtb ")) { + if(line.length() <= 3 || ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR; + try { + double buffer = Double.parseDouble(ctx.buffer); + double val = Double.parseDouble(line.substring(3)); + ctx.buffer = val > buffer ? "true" : "false"; + } catch(Exception ex) { return EnumStatementReturn.PARAMETER_ERROR; } + return EnumStatementReturn.OK; + } + + // lower than buffer + if(lower.startsWith("ltb ")) { + if(line.length() <= 3 || ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR; + try { + double buffer = Double.parseDouble(ctx.buffer); + double val = Double.parseDouble(line.substring(3)); + ctx.buffer = val < buffer ? "true" : "false"; + } catch(Exception ex) { return EnumStatementReturn.PARAMETER_ERROR; } + return EnumStatementReturn.OK; + } + + // greater than or equal buffer + if(lower.startsWith("geb ")) { + if(line.length() <= 3 || ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR; + try { + double buffer = Double.parseDouble(ctx.buffer); + double val = Double.parseDouble(line.substring(3)); + ctx.buffer = val >= buffer ? "true" : "false"; + } catch(Exception ex) { return EnumStatementReturn.PARAMETER_ERROR; } + return EnumStatementReturn.OK; + } + + // lower than or equal buffer + if(lower.startsWith("leb ")) { + if(line.length() <= 3 || ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR; + try { + double buffer = Double.parseDouble(ctx.buffer); + double val = Double.parseDouble(line.substring(3)); + ctx.buffer = val <= buffer ? "true" : "false"; + } catch(Exception ex) { return EnumStatementReturn.PARAMETER_ERROR; } + return EnumStatementReturn.OK; + } + + // sends an RoR signal using the buffer's contents as the message over the supplied channel + if(lower.startsWith("send ")) { + if(line.length() <= 5 || ctx.buffer.isEmpty()) return EnumStatementReturn.PARAMETER_ERROR; + RTTYSystem.broadcast(ctx.world, line.substring(5), ctx.buffer); + return EnumStatementReturn.OK; + } + + // listnes to an RoR signal using the buffer as the channel, then saves the signal to the buffer + if(lower.equals("listen")) { + // this picks up expired signals too, so polling has to be implemented by the user + RTTYChannel chan = RTTYSystem.listen(ctx.world, ctx.buffer); + if(chan != null) ctx.buffer = chan.signal + ""; + return EnumStatementReturn.OK; + } + + return EnumStatementReturn.UNRECOGNIZED_COMMAND; + } + + public String substitute(ParseContext ctx, String statement) { + if(!statement.contains("$")) return statement; + + String[] frags = statement.split("\\$"); + if(frags.length % 2 == 0 || frags.length < 3) return statement; + + // since var names are enclosed with $ signs, we assume that every evenly numbered fragment is a var name + // example: 5 + $val1$ * $val2$ / (-$val3$) + // equals "5 + ", "val1", "* ", "val2", "/ (-", "val3", ")" + // 1 2 3 4 5 6 7 + for(int i = 1; i < frags.length; i += 2) { + // special case, if we try to substitute $buffer$ then read the literal buffer + if(frags[i].equals("buffer")) { + frags[i] = ctx.buffer; + } else { + frags[i] = ctx.variables.getString(frags[i]); + } + } + + return String.join("", frags); + } + + @Override + public void generateJumpPoints(ParseContext ctx, String line, int index) { + if(line.startsWith("dest ") && line.length() > 5) { + ctx.jmp.put(line.substring(5), index); + } } } diff --git a/src/main/java/com/hbm/tileentity/network/TileEntityRadioAUTOCAL.java b/src/main/java/com/hbm/tileentity/network/TileEntityRadioAUTOCAL.java index be2c67a75..d2f2d7d7f 100644 --- a/src/main/java/com/hbm/tileentity/network/TileEntityRadioAUTOCAL.java +++ b/src/main/java/com/hbm/tileentity/network/TileEntityRadioAUTOCAL.java @@ -1,9 +1,11 @@ package com.hbm.tileentity.network; -import java.util.HashMap; - import com.hbm.interfaces.IControlReceiver; import com.hbm.inventory.gui.GUIScreenRadioAUTOCAL; +import com.hbm.module.IParse; +import com.hbm.module.IParse.EnumStatementReturn; +import com.hbm.module.IParse.ParseContext; +import com.hbm.module.ParseMSES1; import com.hbm.tileentity.IGUIProvider; import com.hbm.tileentity.TileEntityTickingBase; @@ -11,6 +13,8 @@ import io.netty.buffer.ByteBuf; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.Container; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.nbt.NBTTagString; import net.minecraft.world.World; public class TileEntityRadioAUTOCAL extends TileEntityTickingBase implements IControlReceiver, IGUIProvider { @@ -19,10 +23,11 @@ public class TileEntityRadioAUTOCAL extends TileEntityTickingBase implements ICo public boolean ignoreError = false; public boolean autoReboot = false; - public Object buffer; - public NBTTagCompound variables; - public String[] script; - public HashMap jmp = new HashMap(); + public static final int MAX_CLOCK_SPEED = 5; + + public String[] script = new String[0]; + public IParse msesv1 = new ParseMSES1(); + public ParseContext ctx; @Override public String getInventoryName() { @@ -33,9 +38,53 @@ public class TileEntityRadioAUTOCAL extends TileEntityTickingBase implements ICo public void updateEntity() { if(!worldObj.isRemote) { + + if(this.worldObj.getTotalWorldTime() % 60 == 0) this.markChanged(); // ensure we're always saved to disk + + if(this.ctx == null) { + this.ctx = new ParseContext(worldObj); + } + if(this.ctx.world != this.worldObj) this.ctx.world = this.worldObj; + + if(!this.isOn && this.autoReboot) { + this.isOn = true; + } + + if(this.isOn) { + + int emergencyBrake = 100; + for(int i = 0; i < this.ctx.clockSpeed && emergencyBrake > 0; i++) { + emergencyBrake--; + + if(this.ctx.current == this.script.length) { this.stop("Program has terminated"); break; } + if(this.ctx.current < 0 || this.ctx.current >= this.script.length) { this.stop("Program index is out of bounds"); break; } + + try { + int index = this.ctx.current; + this.ctx.current ++; + EnumStatementReturn ret = msesv1.eval(ctx, this.script[index]); + if(ret == EnumStatementReturn.END_TICK) break; + if(ret == EnumStatementReturn.SHUTDOWN) this.stop("Program requested shutdown"); + if(!this.ignoreError) { + if(ret == EnumStatementReturn.UNRECOGNIZED_COMMAND) this.stop("Unrecognized command"); + if(ret == EnumStatementReturn.PARAMETER_ERROR) this.stop("Parameter error"); + if(ret == EnumStatementReturn.UNDEFINED) this.stop("Undefined behavior"); + } + if(ret == EnumStatementReturn.SKIP) i--; + } catch(Exception ex) { + this.stop("Evaluation unsuccessful"); + } + } + } + this.networkPackNT(15); } } + + public void stop(String reason) { + this.isOn = false; + this.ctx.turnOff(); + } @Override public void serialize(ByteBuf buf) { @@ -53,6 +102,49 @@ public class TileEntityRadioAUTOCAL extends TileEntityTickingBase implements ICo this.autoReboot = buf.readBoolean(); } + @Override + public void readFromNBT(NBTTagCompound nbt) { + super.readFromNBT(nbt); + + this.isOn = nbt.getBoolean("isOn"); + this.ignoreError = nbt.getBoolean("ignoreError"); + this.autoReboot = nbt.getBoolean("autoReboot"); + + this.ctx = new ParseContext(null); + this.ctx.current = nbt.getInteger("current"); + this.ctx.clockSpeed = nbt.getInteger("clockSpeed"); + this.ctx.buffer = nbt.getString("buffer"); + + NBTTagList lineList = nbt.getTagList("script", 8); + this.script = new String[lineList.tagList.size()]; + for(int i = 0; i < script.length; i++) { + this.script[i] = lineList.getStringTagAt(i); + this.msesv1.generateJumpPoints(ctx, script[i], i); + } + + this.ctx.variables = nbt.getCompoundTag("variables"); + } + + @Override + public void writeToNBT(NBTTagCompound nbt) { + super.writeToNBT(nbt); + + nbt.setBoolean("isOn", isOn); + nbt.setBoolean("ignoreError", ignoreError); + nbt.setBoolean("autoReboot", autoReboot); + + nbt.setInteger("current", ctx.current); + nbt.setInteger("clockSpeed", ctx.clockSpeed); + nbt.setString("buffer", ctx.buffer); + + NBTTagList lineList = new NBTTagList(); + for(String line : this.script) { + lineList.appendTag(new NBTTagString(line)); + } + nbt.setTag("script", lineList); + nbt.setTag("variables", this.ctx.variables); + } + @Override public Container provideContainer(int ID, EntityPlayer player, World world, int x, int y, int z) { return null; } @Override public Object provideGUI(int ID, EntityPlayer player, World world, int x, int y, int z) { return new GUIScreenRadioAUTOCAL(this); } @@ -63,10 +155,23 @@ public class TileEntityRadioAUTOCAL extends TileEntityTickingBase implements ICo @Override public void receiveControl(NBTTagCompound data) { - if(data.hasKey("on")) this.isOn = !this.isOn; + if(data.hasKey("on")) { + if(this.isOn) stop("User requested shutdown"); + else this.isOn = true; + } if(data.hasKey("ignore")) this.ignoreError = !this.ignoreError; if(data.hasKey("auto")) this.autoReboot = !this.autoReboot; + if(data.hasKey("payload")) { + this.ctx.jmp.clear(); + this.script = data.getString("payload").split("\n"); + for(int i = 0; i < script.length; i++) { + script[i] = script[i].trim(); + this.msesv1.generateJumpPoints(ctx, script[i], i); + } + if(this.isOn) stop("Script has changed"); + } + this.markChanged(); } }