feat: implement rudimentary mod loader

This commit is contained in:
TopchetoEU 2024-03-03 23:15:30 +02:00
parent 67bca8c9e0
commit 382122c32f
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
8 changed files with 228 additions and 123 deletions

View File

@ -1,5 +1,5 @@
plugins {
id 'fabric-loom' version '1.0-SNAPSHOT'
id 'fabric-loom' version '1.5-SNAPSHOT'
id 'maven-publish'
}
@ -13,7 +13,7 @@ dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
implementation 'com.github.topchetoeu:jscript:v0.8.7-beta'
implementation 'com.github.topchetoeu:jscript:v0.9.2-beta'
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
modImplementation "com.terraformersmc:modmenu:${project.modmenu_version}"

View File

@ -14,6 +14,6 @@ modmenu_version=6.3.1
project_version = 0.0.1-alpha
project_name = mcscript
project_group me.topchetoeu
project_group = me.topchetoeu
# Dependencies

View File

@ -17,8 +17,9 @@
"fabricloader": ">=0.14.11",
"fabric-api": "*",
"minecraft": "~1.19.3",
"modmenu": "~1.19.3",
"java": ">=17"
},
"suggests": { }
"suggests": {
"modmenu": "*"
}
}

View File

@ -2,7 +2,7 @@ package me.topchetoeu.mcscript;
import java.util.Map;
import me.topchetoeu.jscript.core.engine.Context;
import me.topchetoeu.jscript.core.Context;
public interface EnvironmentFactory {
Iterable<String> dependancies();

View File

@ -1,141 +1,121 @@
package me.topchetoeu.mcscript;
import static com.mojang.brigadier.arguments.StringArgumentType.getString;
import static com.mojang.brigadier.arguments.StringArgumentType.greedyString;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.core.engine.Context;
import me.topchetoeu.jscript.core.engine.Engine;
import me.topchetoeu.jscript.core.engine.Environment;
import me.topchetoeu.jscript.core.engine.values.Values;
import me.topchetoeu.jscript.lib.Internals;
import me.topchetoeu.jscript.utils.filesystem.File;
import me.topchetoeu.jscript.utils.filesystem.Filesystem;
import me.topchetoeu.jscript.utils.filesystem.LineReader;
import me.topchetoeu.jscript.utils.filesystem.LineWriter;
import me.topchetoeu.jscript.utils.filesystem.RootFilesystem;
import me.topchetoeu.jscript.utils.filesystem.STDFilesystem;
import me.topchetoeu.jscript.utils.permissions.PermissionsManager;
import me.topchetoeu.jscript.utils.permissions.PermissionsProvider;
import me.topchetoeu.mcscript.events.ChatMessageCallback;
import me.topchetoeu.mcscript.gui.DevScreen;
import me.topchetoeu.mcscript.core.ModManager;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.minecraft.client.MinecraftClient;
import net.minecraft.server.command.CommandManager;
import net.minecraft.text.Text;
public class McScript implements ModInitializer, ClientModInitializer {
private boolean openDev = false;
// private boolean openDev = false;
public static final Logger LOGGER = LoggerFactory.getLogger("jscript");
private void execute(int i, Environment env, Engine engine, String raw, MessageReceiver msg) {
try {
var res = engine.pushMsg(false, env, new Filename("repl", i + ".js"), raw, null).await();
msg.sendInfo(Values.toReadable(new Context(engine, env), res));
}
catch (RuntimeException e) {
msg.sendError(Values.errorToReadable(e, ""));
}
}
// private void execute(int i, Environment env, Engine engine, String raw, MessageReceiver msg) {
// try {
// var res = engine.pushMsg(false, env, new Filename("repl", i + ".js"), raw, null).await();
// msg.sendInfo(Values.toReadable(new Context(env), res));
// }
// catch (RuntimeException e) {
// msg.sendError(Values.errorToReadable(e, ""));
// }
// }
private Environment createEnv(String location, LineReader in, LineWriter out) {
var env = new Environment();
var inFile = File.ofLineReader(in);
var outFile = File.ofLineWriter(out);
Internals.apply(env);
// private Environment createEnv(String location, LineReader in, LineWriter out) {
// var env = new Environment();
// var inFile = File.ofLineReader(in);
// var outFile = File.ofLineWriter(out);
// Internals.apply(env);
var fs = new RootFilesystem(PermissionsProvider.get(env));
fs.protocols.put("std", new STDFilesystem().add("in", inFile).add("out", outFile));
// var fs = new RootFilesystem(PermissionsProvider.get(env));
// fs.protocols.put("std", new STDFilesystem().add("in", inFile).add("out", outFile));
var perms = new PermissionsManager();
// var perms = new PermissionsManager();
env.add(PermissionsProvider.ENV_KEY, perms);
env.add(Filesystem.ENV_KEY, fs);
// env.add(PermissionsProvider.KEY, perms);
// env.add(Filesystem.KEY, fs);
env.global.define(null, "env", true, location);
return env;
}
// env.global.define(null, "env", true, location);
// return env;
// }
@Override public void onInitialize() {
var engine = new Engine();
var mods = new ModManager("mods", "mod-data");
try {
mods.load();
}
catch (IOException e) {
e.printStackTrace();
}
ServerLifecycleEvents.SERVER_STARTING.register(server -> {
engine.start();
// ServerLifecycleEvents.CLIENT_STARTED.register((client) -> {
// var receiver = new ClientMessageReceiver();
// mods.setSTD(null, File.ofLineWriter(receiver::sendInfo), File.ofLineWriter(receiver::sendError));
mods.setSTD(null, File.ofLineWriter(LOGGER::info), File.ofLineWriter(LOGGER::info));
mods.start();
// });
var env = createEnv("SERVER", () -> null, value -> {
for (var line : value.split("\n", -1)) LOGGER.info(line);
});
// var engine = new Engine();
var i = new int[1];
// ServerLifecycleEvents.SERVER_STARTING.register(server -> {
// engine.start();
server.getCommandManager().getDispatcher().register(CommandManager.literal("msc")
.then(CommandManager.argument("code", greedyString()).executes(c -> {
String str = getString(c, "code");
var receiver = new ServerCommandMessageReceiver(c.getSource());
execute(i[0]++, env, engine, str, receiver);
return 1;
}))
);
});
ServerLifecycleEvents.SERVER_STOPPED.register(server -> {
engine.stop();
});
// var env = createEnv("SERVER", () -> null, value -> {
// for (var line : value.split("\n", -1)) LOGGER.info(line);
// });
// var i = new int[1];
// server.getCommandManager().getDispatcher().register(CommandManager.literal("msc")
// .then(CommandManager.argument("code", greedyString()).executes(c -> {
// String str = getString(c, "code");
// var receiver = new ServerCommandMessageReceiver(c.getSource());
// execute(i[0]++, env, engine, str, receiver);
// return 1;
// }))
// );
// });
// ServerLifecycleEvents.SERVER_STOPPED.register(server -> {
// engine.stop();
// });
}
@net.fabricmc.api.Environment(EnvType.CLIENT)
@Override public void onInitializeClient() {
var dev = new DevScreen();
var engine = new Engine();
// var dev = new DevScreen();
// var engine = new Engine();
ClientLifecycleEvents.CLIENT_STARTED.register((client) -> {
try {
Files.createDirectories(Path.of("scripts"));
}
catch (IOException e) { /* so be it */ }
var receiver = new ClientMessageReceiver();
var env = createEnv("CLIENT", () -> null, value -> MinecraftClient.getInstance().player.sendMessage(Text.of(value)));
engine.start();
var i = new int[1];
ChatMessageCallback.EVENT.register(args -> {
if (args.message.startsWith("#")) {
execute(i[0]++, env, engine, args.message.substring(1), receiver);
args.cancelled = true;
}
});
ClientTickEvents.END_CLIENT_TICK.register((_1) -> {
if (openDev) {
client.setScreenAndRender(dev);
openDev = false;
}
});
ClientCommandRegistrationCallback.EVENT.register((disp, _1) -> {
disp.register(ClientCommandManager.literal("dev")
.executes(c -> {
openDev = true;
return 1;
})
);
});
// var i = new int[1];
// ChatMessageCallback.EVENT.register(args -> {
// if (args.message.startsWith("#")) {
// execute(i[0]++, env, engine, args.message.substring(1), receiver);
// args.cancelled = true;
// }
// });
// ClientTickEvents.END_CLIENT_TICK.register((_1) -> {
// if (openDev) {
// client.setScreenAndRender(dev);
// openDev = false;
// }
// });
// ClientCommandRegistrationCallback.EVENT.register((disp, _1) -> {
// disp.register(ClientCommandManager.literal("dev")
// .executes(c -> {
// openDev = true;
// return 1;
// })
// );
// });
});
ClientLifecycleEvents.CLIENT_STOPPING.register((client) -> {
engine.stop();
});
// ClientLifecycleEvents.CLIENT_STOPPING.register((client) -> {
// engine.stop();
// });
}
}

View File

@ -0,0 +1,26 @@
package me.topchetoeu.mcscript.core;
import me.topchetoeu.jscript.core.Engine;
import me.topchetoeu.jscript.core.Environment;
import me.topchetoeu.jscript.core.EventLoop;
import me.topchetoeu.jscript.utils.filesystem.RootFilesystem;
public class Mod {
public final String name;
public final String author;
public final String version;
public final Engine loop;
public final Environment environment;
public final RootFilesystem fs;
public Mod(String name, String author, String version, Engine loop, Environment environment, RootFilesystem fs) {
this.name = name;
this.author = author;
this.version = version;
this.loop = loop;
this.fs = fs;
this.environment = environment;
this.environment.add(EventLoop.KEY, loop);
}
}

View File

@ -0,0 +1,103 @@
package me.topchetoeu.mcscript.core;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.json.JSON;
import me.topchetoeu.jscript.core.Compiler;
import me.topchetoeu.jscript.core.Engine;
import me.topchetoeu.jscript.core.Environment;
import me.topchetoeu.jscript.core.EventLoop;
import me.topchetoeu.jscript.lib.Internals;
import me.topchetoeu.jscript.utils.JSCompiler;
import me.topchetoeu.jscript.utils.filesystem.File;
import me.topchetoeu.jscript.utils.filesystem.Filesystem;
import me.topchetoeu.jscript.utils.filesystem.PhysicalFilesystem;
import me.topchetoeu.jscript.utils.filesystem.RootFilesystem;
import me.topchetoeu.jscript.utils.filesystem.STDFilesystem;
import me.topchetoeu.jscript.utils.modules.ModuleRepo;
import me.topchetoeu.jscript.utils.modules.RootModuleRepo;
import me.topchetoeu.jscript.utils.permissions.PermissionsManager;
import me.topchetoeu.jscript.utils.permissions.PermissionsProvider;
public class ModManager {
public final String codeFolder, dataFolder;
public final List<Mod> mods = new ArrayList<>();
private void loadMod(String folder) throws IOException {
var filename = Path.of(folder, "manifest.json");
if (!filename.toFile().exists() || !filename.toFile().isFile()) return;
var rawManifest = new String(Files.readAllBytes(filename));
var manifest = JSON.parse(Filename.fromFile(filename.toFile()), rawManifest).map();
var name = manifest.string("name");
var version = manifest.string("version");
var author = manifest.string("author");
var main = manifest.string("main");
var mainSrc = new String(Files.readAllBytes(Path.of(codeFolder.toString(), name, main)));
var env = new Environment();
var loop = new Engine();
var fs = new RootFilesystem(PermissionsProvider.get(env));
fs.protocols.put("file", new PhysicalFilesystem(Path.of(dataFolder.toString(), name).toString()));
fs.protocols.put("code", new PhysicalFilesystem(Path.of(codeFolder.toString(), name).toString()));
Files.createDirectories(Path.of(codeFolder.toString(), name));
var perms = new PermissionsManager();
perms.add("jscript.file.read:**");
perms.add("jscript.file.read:std://in");
perms.add("jscript.file.write:std://out");
perms.add("jscript.file.write:std://err");
perms.add("jscript.file.write:file://**");
var modules = new RootModuleRepo();
modules.repos.put("file", ModuleRepo.ofFilesystem(fs.protocols.get("code")));
Internals.apply(env);
env.add(EventLoop.KEY, loop);
env.add(Filesystem.KEY, fs);
env.add(PermissionsProvider.KEY, perms);
env.add(Compiler.KEY, new JSCompiler(env));
loop.pushMsg(false, env, new Filename("code", main), mainSrc, null);
var mod = new Mod(name, author, version, loop, env, fs);
mods.add(mod);
}
public void load() throws IOException {
for (var file : Files.list(Path.of(codeFolder)).collect(Collectors.toList())) {
if (!Files.isDirectory(file)) continue;
loadMod(file.toString());
}
}
public void setSTD(File in, File out, File err) {
var std = new STDFilesystem();
std.add("in", in);
std.add("out", out);
std.add("err", err);
for (var mod : mods) {
mod.fs.protocols.put("std", std);
}
}
public void start() {
for (var mod : mods) {
mod.loop.start();
}
}
public ModManager(String folder, String dataFolder) {
this.codeFolder = folder;
this.dataFolder = dataFolder;
}
}

View File

@ -1,5 +0,0 @@
package me.topchetoeu.mcscript.loader;
public interface ModLoader {
}