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 ->
|
||||||
builder.group(Codec.unboundedMap(Codec.STRING, Codec.lazyInitialized(() -> AjarFile.CODEC))
|
builder.group(Codec.unboundedMap(Codec.STRING, Codec.lazyInitialized(() -> AjarFile.CODEC))
|
||||||
.fieldOf("listing").forGetter(AjarDirectory::listing))
|
.fieldOf("listing").forGetter(AjarDirectory::listing))
|
||||||
.apply(builder, AjarDirectory::new));
|
.apply(builder, map -> new AjarDirectory(new HashMap<>(map))));
|
||||||
|
|
||||||
public static AjarDirectory ofEmpty() {
|
public static AjarDirectory ofEmpty() {
|
||||||
return new AjarDirectory(new HashMap<>());
|
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.register.Register;
|
||||||
import dev.exhq.ajarc.vm.JsVm;
|
import dev.exhq.ajarc.vm.JsVm;
|
||||||
//import dev.exhq.ajarc.vm.WasmVm;
|
//import dev.exhq.ajarc.vm.WasmVm;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.HolderLookup;
|
import net.minecraft.core.HolderLookup;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
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.entity.player.Player;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -50,6 +53,10 @@ public class ComputerBlockEntity extends BlockEntity {
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AjarFileSystem getFileSystem() {
|
||||||
|
return fileSystem;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void saveAdditional(@NotNull CompoundTag pTag, HolderLookup.@NotNull Provider pRegistries) {
|
protected void saveAdditional(@NotNull CompoundTag pTag, HolderLookup.@NotNull Provider pRegistries) {
|
||||||
super.saveAdditional(pTag, pRegistries);
|
super.saveAdditional(pTag, pRegistries);
|
||||||
|
@ -83,10 +90,31 @@ public class ComputerBlockEntity extends BlockEntity {
|
||||||
} else if (line.equals("big")) {
|
} else if (line.equals("big")) {
|
||||||
screen = ComputerTerminal.ofSize(20, 30);
|
screen = ComputerTerminal.ofSize(20, 30);
|
||||||
lines.add("Made big!");
|
lines.add("Made big!");
|
||||||
} else if (line.startsWith("add")) {
|
} else if (line.startsWith("resetfs")) {
|
||||||
lines.add(jsVm.add(69, 42) + "");
|
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 {
|
} 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().rows(),
|
||||||
screen.dimensions().columns());
|
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.NashornSandbox;
|
||||||
import delight.nashornsandbox.NashornSandboxes;
|
import delight.nashornsandbox.NashornSandboxes;
|
||||||
import delight.nashornsandbox.internal.JsSanitizer;
|
|
||||||
import dev.exhq.ajarc.Ajar;
|
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.ComputerBlockEntity;
|
||||||
import dev.exhq.ajarc.computer.ComputerTerminal;
|
import dev.exhq.ajarc.computer.ComputerTerminal;
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
|
|
||||||
import javax.script.ScriptException;
|
import javax.script.ScriptException;
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Map;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class JsVm {
|
public class JsVm {
|
||||||
private final NashornSandbox sandbox;
|
private final NashornSandbox sandbox;
|
||||||
|
private final ComputerBlockEntity entity;
|
||||||
|
|
||||||
public JsVm(ComputerBlockEntity entity) {
|
public JsVm(ComputerBlockEntity entity) {
|
||||||
sandbox = NashornSandboxes.create();
|
this.sandbox = NashornSandboxes.create();
|
||||||
sandbox.inject("screen", new ScreenHelper(entity));
|
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 record ScreenHelper(ComputerBlockEntity entity) {
|
||||||
public void setScreenSize(int rows, int cols) {
|
public void setScreenSize(int rows, int cols) {
|
||||||
entity.setTerminal(ComputerTerminal.ofSize(rows, cols));
|
entity.setTerminal(ComputerTerminal.ofSize(rows, cols));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void print(String line) {
|
||||||
|
entity.addLine(line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int add(int left, int right) {
|
public void executeScript(String source, String[] args) {
|
||||||
String text;
|
|
||||||
try {
|
try {
|
||||||
var stream = Minecraft.getInstance()
|
// TODO: provide an import facility of some sort
|
||||||
.getResourceManager()
|
var bindings = sandbox.createBindings();
|
||||||
.getResourceOrThrow(Ajar.identifier("script.js"))
|
bindings.put("process", new ProcessHelper(args));
|
||||||
.open();
|
sandbox.eval(source, bindings);
|
||||||
text = IOUtils.toString(stream, StandardCharsets.UTF_8);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
var bindings = sandbox.createBindings();
|
|
||||||
bindings.put("left", left);
|
|
||||||
bindings.put("right", right);
|
|
||||||
try {
|
|
||||||
sandbox.eval(text, bindings);
|
|
||||||
} catch (ScriptException e) {
|
} catch (ScriptException e) {
|
||||||
throw new RuntimeException(e);
|
Ajar.LOGGER.error("Could not execute script", e);
|
||||||
|
entity.addLine("Could not execute script: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return (int) (double) bindings.get("result");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue