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 { plugins {
id 'fabric-loom' version '1.0-SNAPSHOT' id 'fabric-loom' version '1.5-SNAPSHOT'
id 'maven-publish' id 'maven-publish'
} }
@ -13,7 +13,7 @@ dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}" minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" 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-loader:${project.loader_version}"
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
modImplementation "com.terraformersmc:modmenu:${project.modmenu_version}" modImplementation "com.terraformersmc:modmenu:${project.modmenu_version}"

View File

@ -1,19 +1,19 @@
# Done to increase the memory available to gradle. # Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx4G org.gradle.jvmargs = -Xmx4G
org.gradle.parallel=true org.gradle.parallel = true
# Dependencies # Dependencies
minecraft_version=1.19.4 minecraft_version = 1.19.4
yarn_mappings=1.19.4+build.2 yarn_mappings = 1.19.4+build.2
loader_version=0.15.3 loader_version = 0.15.3
fabric_version=0.87.2+1.19.4 fabric_version = 0.87.2+1.19.4
modmenu_version=6.3.1 modmenu_version = 6.3.1
# Project properties # Project properties
project_version=0.0.1-alpha project_version = 0.0.1-alpha
project_name = mcscript project_name = mcscript
project_group me.topchetoeu project_group = me.topchetoeu
# Dependencies # Dependencies

View File

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

View File

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

View File

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