Add shitty filesystem executor
This commit is contained in:
parent
5545dc1ab8
commit
7c76b5ca1a
3 changed files with 160 additions and 29 deletions
|
@ -14,7 +14,7 @@ public record AjarDirectory(
|
|||
builder ->
|
||||
builder.group(Codec.unboundedMap(Codec.STRING, Codec.lazyInitialized(() -> AjarFile.CODEC))
|
||||
.fieldOf("listing").forGetter(AjarDirectory::listing))
|
||||
.apply(builder, AjarDirectory::new));
|
||||
.apply(builder, map -> new AjarDirectory(new HashMap<>(map))));
|
||||
|
||||
public static AjarDirectory ofEmpty() {
|
||||
return new AjarDirectory(new HashMap<>());
|
||||
|
|
|
@ -6,6 +6,7 @@ import dev.exhq.ajarc.network.ComputerScreenUpdate;
|
|||
import dev.exhq.ajarc.register.Register;
|
||||
import dev.exhq.ajarc.vm.JsVm;
|
||||
//import dev.exhq.ajarc.vm.WasmVm;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
@ -15,8 +16,10 @@ import net.minecraft.world.SimpleMenuProvider;
|
|||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
@ -50,6 +53,10 @@ public class ComputerBlockEntity extends BlockEntity {
|
|||
.getOrThrow();
|
||||
}
|
||||
|
||||
public AjarFileSystem getFileSystem() {
|
||||
return fileSystem;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveAdditional(@NotNull CompoundTag pTag, HolderLookup.@NotNull Provider pRegistries) {
|
||||
super.saveAdditional(pTag, pRegistries);
|
||||
|
@ -83,10 +90,31 @@ public class ComputerBlockEntity extends BlockEntity {
|
|||
} else if (line.equals("big")) {
|
||||
screen = ComputerTerminal.ofSize(20, 30);
|
||||
lines.add("Made big!");
|
||||
} else if (line.startsWith("add")) {
|
||||
lines.add(jsVm.add(69, 42) + "");
|
||||
} else if (line.startsWith("resetfs")) {
|
||||
var root = fileSystem.root().listing();
|
||||
var directory = (AjarDirectory) root.compute("bin", (ignored, old) ->
|
||||
old instanceof AjarDirectory ? old : AjarDirectory.ofEmpty());
|
||||
Minecraft.getInstance().getResourceManager()
|
||||
.listResources("bin", pred -> pred.getNamespace().equals(Ajar.MODID) && pred.getPath().endsWith(".js"))
|
||||
.forEach(
|
||||
(resourceLocation, resource) ->
|
||||
{
|
||||
try {
|
||||
directory.listing().put(
|
||||
resourceLocation.getPath().replace("bin/", ""),
|
||||
new AjarRegularFile(IOUtils.toByteArray(resource.open()))
|
||||
);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
lines.add("Made unknown!");
|
||||
var bin = (AjarDirectory) fileSystem.root().listing().get("bin");
|
||||
var parts = line.split(" ");
|
||||
var commandFile =(AjarRegularFile) bin.listing().get(parts[0] + ".js");
|
||||
var commandText = commandFile.getTextContent();
|
||||
jsVm.executeScript(commandText, Arrays.copyOfRange(parts, 1, parts.length));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,4 +124,8 @@ public class ComputerBlockEntity extends BlockEntity {
|
|||
screen.dimensions().rows(),
|
||||
screen.dimensions().columns());
|
||||
}
|
||||
|
||||
public void addLine(String line) {
|
||||
lines.add(line);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,52 +2,151 @@ package dev.exhq.ajarc.vm;
|
|||
|
||||
import delight.nashornsandbox.NashornSandbox;
|
||||
import delight.nashornsandbox.NashornSandboxes;
|
||||
import delight.nashornsandbox.internal.JsSanitizer;
|
||||
import dev.exhq.ajarc.Ajar;
|
||||
import dev.exhq.ajarc.computer.AjarDirectory;
|
||||
import dev.exhq.ajarc.computer.AjarFile;
|
||||
import dev.exhq.ajarc.computer.AjarRegularFile;
|
||||
import dev.exhq.ajarc.computer.AjarSymlinkFile;
|
||||
import dev.exhq.ajarc.computer.ComputerBlockEntity;
|
||||
import dev.exhq.ajarc.computer.ComputerTerminal;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import javax.script.ScriptException;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class JsVm {
|
||||
private final NashornSandbox sandbox;
|
||||
private final ComputerBlockEntity entity;
|
||||
|
||||
public JsVm(ComputerBlockEntity entity) {
|
||||
sandbox = NashornSandboxes.create();
|
||||
sandbox.inject("screen", new ScreenHelper(entity));
|
||||
this.sandbox = NashornSandboxes.create();
|
||||
this.entity = entity;
|
||||
this.sandbox.inject("screen", new ScreenHelper(entity));
|
||||
this.sandbox.inject("fs", new FileSystemHelper(entity));
|
||||
this.sandbox.allow(ProcessHelper.class);
|
||||
}
|
||||
|
||||
public record ProcessHelper(String[] args) {
|
||||
}
|
||||
|
||||
private record BoundFile(AjarDirectory parent, String name) {
|
||||
public boolean exists() {
|
||||
return parent.listing().containsKey(name);
|
||||
}
|
||||
|
||||
public AjarFile resolve() {
|
||||
return parent.listing().get(name);
|
||||
}
|
||||
|
||||
public void set(AjarFile file) {
|
||||
parent.listing().put(name, file);
|
||||
}
|
||||
|
||||
public void unlink() {
|
||||
parent.listing().remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
public record FileSystemHelper(ComputerBlockEntity entity) {
|
||||
private List<String> splitPath(String path) {
|
||||
var array = new ArrayList<>(Arrays.asList(path.split("[/\\\\]")));
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
var str = array.get(i);
|
||||
if ("..".equals(str) && i > 1) {
|
||||
array.remove(i);
|
||||
array.remove(i - 1);
|
||||
i--;
|
||||
} else if (".".equals(str) || "".equals(str)) {
|
||||
array.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
private BoundFile resolveFile(
|
||||
String path
|
||||
) {
|
||||
AjarDirectory node = entity.getFileSystem().root();
|
||||
List<String> splitPath = splitPath(path);
|
||||
if (splitPath.isEmpty())
|
||||
return null;
|
||||
for (int i = 0; i < splitPath.size(); i++) {
|
||||
var segment = splitPath.get(i);
|
||||
var child = node.listing().get(segment);
|
||||
if (child == null) return null;
|
||||
if (child instanceof AjarSymlinkFile symlink) {
|
||||
// TODO: symlink infinite recursion protection
|
||||
if (symlink.target().startsWith("/")) {
|
||||
return resolveFile(symlink.target() + "/" + String.join("/", splitPath.subList(i + 1, splitPath.size())));
|
||||
} else {
|
||||
return resolveFile(String.join("/", splitPath.subList(0, i)) + "/" + symlink.target() + splitPath.subList(i + 1, splitPath.size()));
|
||||
}
|
||||
}
|
||||
if (i == splitPath.size() - 1) break;
|
||||
if (!(child instanceof AjarDirectory directory)) {
|
||||
throw new RuntimeException("No such file or directory " + path);
|
||||
}
|
||||
node = directory;
|
||||
}
|
||||
return new BoundFile(node, splitPath.getLast());
|
||||
}
|
||||
|
||||
public boolean unlink(String path) {
|
||||
var boundFile = resolveFile(path);
|
||||
if (boundFile == null || !boundFile.exists())
|
||||
return false;
|
||||
boundFile.unlink();
|
||||
return true;
|
||||
}
|
||||
|
||||
public String readFile(String path) {
|
||||
// TODO: resolve relative to working directory
|
||||
var boundFile = resolveFile(path);
|
||||
if (boundFile == null || !boundFile.exists())
|
||||
throw new RuntimeException("No such file: " + path);
|
||||
var file = boundFile.resolve();
|
||||
if (file instanceof AjarRegularFile regularFile) {
|
||||
return regularFile.getTextContent();
|
||||
}
|
||||
throw new RuntimeException("File is not a text file");
|
||||
}
|
||||
|
||||
public void writeFile(String path, String content) {
|
||||
var boundFile = resolveFile(path);
|
||||
if (boundFile == null) {
|
||||
throw new RuntimeException("Could not resolve file (parent directory does not exist): " + path);
|
||||
}
|
||||
var existingFile = boundFile.resolve();
|
||||
if (existingFile != null && !(existingFile instanceof AjarRegularFile)) {
|
||||
throw new RuntimeException("Cannot overwrite non text file: " + path);
|
||||
}
|
||||
boundFile.set(new AjarRegularFile(content.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public record ScreenHelper(ComputerBlockEntity entity) {
|
||||
public void setScreenSize(int rows, int cols) {
|
||||
entity.setTerminal(ComputerTerminal.ofSize(rows, cols));
|
||||
}
|
||||
|
||||
public void print(String line) {
|
||||
entity.addLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
public int add(int left, int right) {
|
||||
String text;
|
||||
public void executeScript(String source, String[] args) {
|
||||
try {
|
||||
var stream = Minecraft.getInstance()
|
||||
.getResourceManager()
|
||||
.getResourceOrThrow(Ajar.identifier("script.js"))
|
||||
.open();
|
||||
text = IOUtils.toString(stream, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
// TODO: provide an import facility of some sort
|
||||
var bindings = sandbox.createBindings();
|
||||
bindings.put("left", left);
|
||||
bindings.put("right", right);
|
||||
try {
|
||||
sandbox.eval(text, bindings);
|
||||
bindings.put("process", new ProcessHelper(args));
|
||||
sandbox.eval(source, bindings);
|
||||
} catch (ScriptException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return (int) (double) bindings.get("result");
|
||||
Ajar.LOGGER.error("Could not execute script", e);
|
||||
entity.addLine("Could not execute script: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue