diff --git a/src/main/interpreter/Interpreter.java b/src/main/interpreter/Interpreter.java index 320b2b2..fae6143 100644 --- a/src/main/interpreter/Interpreter.java +++ b/src/main/interpreter/Interpreter.java @@ -2,6 +2,7 @@ import interpreter.antlr.MineScriptLexer; import interpreter.antlr.MineScriptParser; +import interpreter.exceptions.ThreadInterruptedException; import interpreter.types.MSMessageType; import minescript.network.TurtleCommands; import net.minecraft.server.MinecraftServer; @@ -27,6 +28,7 @@ public Interpreter(String program, MinecraftServer server, ServerWorld world, Bl @Override public void run() { + System.out.println("Thread started"); try { // Create a CharStream that reads from standard input CharStream input = CharStreams.fromString(program + System.lineSeparator()); @@ -43,8 +45,11 @@ public void run() { ParseTree tree = parser.program(); // Begin parsing at init rule Visitor visitor = new Visitor(server, world, turtlePos, new SymbolTable()); visitor.visit(tree); + } catch (ThreadInterruptedException e) { + System.out.println(e.getMessage()); } catch (Exception e) { TurtleCommands.print(server, e.getMessage(), MSMessageType.ERROR); } + System.out.println("Thread finished"); } } \ No newline at end of file diff --git a/src/main/interpreter/SymbolTable.java b/src/main/interpreter/SymbolTable.java index ec506a9..99244e3 100644 --- a/src/main/interpreter/SymbolTable.java +++ b/src/main/interpreter/SymbolTable.java @@ -35,12 +35,13 @@ public void enterSymbol(String name, MSType value) { Symbol newSymbol = new Symbol(name, value); checkRestrictedKeyWords(newSymbol); - /*If the variable is already in the current scope, update it*/ - if (isVarInNewScope(name)) { - Symbol oldSymbol = hashMap.get(getPrefixName(name)); + /*If a prefixed version of the variable already exists, update it*/ + String shadowedSymbolName = getShadowedSymbolName(name); + if (shadowedSymbolName != null) { + Symbol oldSymbol = hashMap.get(shadowedSymbolName); delete(oldSymbol.name); - Symbol prefixSymbol = new Symbol(oldSymbol.name, value); - add(prefixSymbol); + Symbol newShadowedSymbol = new Symbol(oldSymbol.name, value); + add(newShadowedSymbol); return; } @@ -77,8 +78,9 @@ else if (symbol.name.equals(funcName.name())){ * @return symbol from the hash table */ public Symbol retrieveSymbol(String name) { - if (isVarInNewScope(name)) { - return hashMap.get(getPrefixName(name)); + String shadowedSymbolName = getShadowedSymbolName(name); + if (shadowedSymbolName != null) { + return hashMap.get(shadowedSymbolName); } else if (hashMap.containsKey(name)) { return hashMap.get(name); } else { @@ -108,20 +110,23 @@ private void add(Symbol symbol) { hashMap.put(symbol.name, symbol); } - /** - * @param name name of the variable - * @return true if the variable is in the current scope - */ - private boolean isVarInNewScope(String name) { - return scopeStack.peek().stream().anyMatch(s -> s.endsWith("." + name)); - } - /** * @param name id of the variable - * @return prefix of the variable + * @return name of the symbol if a prefixed version of it exists in any of the scopes, otherwise null */ - private String getPrefixName(String name) { - return scopeStack.peek().stream().filter(s -> s.contains("." + name)).findFirst().orElseThrow(); + private String getShadowedSymbolName(String name) { + if (scopeStack.empty()) return null; + + ArrayList currentScope = scopeStack.peek(); + if (currentScope.stream().anyMatch(s -> s.endsWith("." + name))) { + return currentScope.stream().filter(s -> s.endsWith("." + name)).findFirst().orElseThrow(); + } + else { + scopeStack.pop(); + String res = getShadowedSymbolName(name); + scopeStack.push(currentScope); + return res; + } } private record Symbol(String name, MSType value) { diff --git a/src/main/interpreter/Visitor.java b/src/main/interpreter/Visitor.java index 9e14a11..aaddecb 100644 --- a/src/main/interpreter/Visitor.java +++ b/src/main/interpreter/Visitor.java @@ -3,6 +3,7 @@ import interpreter.antlr.MineScriptBaseVisitor; import interpreter.antlr.MineScriptParser; import interpreter.exceptions.SymbolNotFoundException; +import interpreter.exceptions.ThreadInterruptedException; import interpreter.types.*; import minescript.network.TurtleCommands; import net.minecraft.block.Block; @@ -446,7 +447,7 @@ public MSType visitFuncCall(MineScriptParser.FuncCallContext ctx) { throw new RuntimeException(getFuncCallErrorMessage(id, new int[]{1}, "number", actualParams)); } - retVal = new MSNumber((int) Math.round(Math.sqrt(n.getValue()))); + retVal = new MSNumber((int) Math.floor(Math.sqrt(n.getValue()))); } case "Random" -> { /*If no parameter is used returns a random unbounded int*/ @@ -477,21 +478,21 @@ public MSType visitFuncCall(MineScriptParser.FuncCallContext ctx) { this.turtleDelay = 200 - n.getValue() * 20; } - case "GetXPosition" -> { + case "GetXCoordinate" -> { if (actualParams.size() != 0) { throw new RuntimeException(getFuncCallErrorMessage(id, new int[]{0}, "", actualParams)); } retVal = new MSNumber(pos.getX()); } - case "GetYPosition" -> { + case "GetYCoordinate" -> { if (actualParams.size() != 0) { throw new RuntimeException(getFuncCallErrorMessage(id, new int[]{0}, "", actualParams)); } retVal = new MSNumber(pos.getY()); } - case "GetZPosition" -> { + case "GetZCoordinate" -> { if (actualParams.size() != 0) { throw new RuntimeException(getFuncCallErrorMessage(id, new int[]{0}, "", actualParams)); } @@ -583,6 +584,7 @@ public MSType visitFuncCall(MineScriptParser.FuncCallContext ctx) { } else { TurtleCommands.print(server, expressionId + " is: " + text, messageType); } + timeout(1); }); } default -> { @@ -763,7 +765,7 @@ private void timeout(int millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { - throw new RuntimeException(e); + throw new ThreadInterruptedException("Thread was interrupted while waiting"); } } } \ No newline at end of file diff --git a/src/main/interpreter/exceptions/ThreadInterruptedException.java b/src/main/interpreter/exceptions/ThreadInterruptedException.java new file mode 100644 index 0000000..f1cea4e --- /dev/null +++ b/src/main/interpreter/exceptions/ThreadInterruptedException.java @@ -0,0 +1,5 @@ +package interpreter.exceptions; + +public class ThreadInterruptedException extends RuntimeException { + public ThreadInterruptedException(String message) { super(message); } +} diff --git a/src/main/java/minescript/block/custom/TurtleBlock.java b/src/main/java/minescript/block/custom/TurtleBlock.java index 2794356..cd77004 100644 --- a/src/main/java/minescript/block/custom/TurtleBlock.java +++ b/src/main/java/minescript/block/custom/TurtleBlock.java @@ -69,10 +69,9 @@ public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockSt BlockEntity blockEntity = world.getBlockEntity(pos); if (blockEntity instanceof TurtleBlockEntity entity) { - PacketByteBuf buf = PacketByteBufs.create(); - buf.writeBlockPos(entity.getPos()); - - ClientPlayNetworking.send(MineScriptPackets.STOP_INTERPRETER_ID, buf); + if (entity.interpreterThread != null && entity.interpreterThread.isAlive()) { + entity.interpreterThread.interrupt(); + } } } super.onStateReplaced(state, world, pos, newState, moved); diff --git a/src/main/java/minescript/block/entity/TurtleBlockEntity.java b/src/main/java/minescript/block/entity/TurtleBlockEntity.java index a6a7694..d6888cc 100644 --- a/src/main/java/minescript/block/entity/TurtleBlockEntity.java +++ b/src/main/java/minescript/block/entity/TurtleBlockEntity.java @@ -33,19 +33,37 @@ public void setTurtleInput(String input) { @Override public void writeNbt(NbtCompound nbt) { - nbt.putString("input", this.input.getString()); + String text = input.getString(); + int length = text.length(); + + int splits = (int) Math.ceil((double) length / 50000); + for (int i = 0; i < splits; i++) { + if (i == splits - 1) + nbt.putString("input" + i, text.substring(i * 50000)); + else + nbt.putString("input" + i, text.substring(i * 50000, (i + 1) * 50000)); + } + super.writeNbt(nbt); } @Override public void readNbt(NbtCompound nbt) { + StringBuilder text = new StringBuilder(); + int split = 0; + while (nbt.contains("input" + split)) { + text.append(nbt.getString("input" + split)); + split++; + } + this.input = Text.of(text.toString()); super.readNbt(nbt); - this.input = Text.of(nbt.getString("input")); } @Override public void writeScreenOpeningData(ServerPlayerEntity player, PacketByteBuf buf) { - buf.writeString(this.input.getString()); + String text = this.input.getString(); + buf.writeInt(text.length()); + buf.writeString(text, text.length()); } @Override diff --git a/src/main/java/minescript/network/MineScriptPackets.java b/src/main/java/minescript/network/MineScriptPackets.java index 6e74104..087a6bd 100644 --- a/src/main/java/minescript/network/MineScriptPackets.java +++ b/src/main/java/minescript/network/MineScriptPackets.java @@ -7,10 +7,8 @@ public class MineScriptPackets { public static final Identifier START_INTERPRETER_ID = new Identifier("minescript", "start_interpreter"); - public static final Identifier STOP_INTERPRETER_ID = new Identifier("minescript", "stop_interpreter"); public static void registerC2SPackets() { ServerPlayNetworking.registerGlobalReceiver(START_INTERPRETER_ID, StartInterpreterC2SPacket::receive); - ServerPlayNetworking.registerGlobalReceiver(STOP_INTERPRETER_ID, StopInterpreterC2SPacket::receive); } } diff --git a/src/main/java/minescript/network/packet/StopInterpreterC2SPacket.java b/src/main/java/minescript/network/packet/StopInterpreterC2SPacket.java deleted file mode 100644 index 0f56a77..0000000 --- a/src/main/java/minescript/network/packet/StopInterpreterC2SPacket.java +++ /dev/null @@ -1,27 +0,0 @@ -package minescript.network.packet; - -import interpreter.Interpreter; -import minescript.block.entity.TurtleBlockEntity; -import net.fabricmc.fabric.api.networking.v1.PacketSender; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.network.ServerPlayNetworkHandler; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.math.BlockPos; - -public class StopInterpreterC2SPacket { - public static void receive(MinecraftServer server, ServerPlayerEntity player, ServerPlayNetworkHandler handler, PacketByteBuf buf, PacketSender responseSender) { - BlockPos pos = buf.readBlockPos(); - - server.executeSync(() -> { - ServerWorld world = player.getWorld(); - TurtleBlockEntity entity = (TurtleBlockEntity) world.getBlockEntity(pos); - - assert entity != null; - if (entity.interpreterThread != null && entity.interpreterThread.isAlive()) { - entity.interpreterThread.stop(); - } - }); - } -} diff --git a/src/main/java/minescript/screen/TextEditorScreenHandler.java b/src/main/java/minescript/screen/TextEditorScreenHandler.java index c5a59d0..391efbf 100644 --- a/src/main/java/minescript/screen/TextEditorScreenHandler.java +++ b/src/main/java/minescript/screen/TextEditorScreenHandler.java @@ -23,7 +23,7 @@ public class TextEditorScreenHandler extends ScreenHandler { public TextEditorScreenHandler(int syncId, PlayerInventory inventory, PacketByteBuf buf) { this(syncId, inventory, (TurtleBlockEntity) null); - inputText = buf.readString(); + inputText = buf.readString(buf.readInt()); } public TextEditorScreenHandler(int syncId, PlayerInventory inventory, @Nullable TurtleBlockEntity entity) {