Respackopts/src/client/java/io/gitlab/jfronny/respackopts/RpoClientCommand.java

138 lines
7.6 KiB
Java

package io.gitlab.jfronny.respackopts;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import io.gitlab.jfronny.commons.throwable.ThrowingConsumer;
import io.gitlab.jfronny.commons.throwable.ThrowingSupplier;
import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.context.ExprUtils;
import io.gitlab.jfronny.muscript.core.LocationalException;
import io.gitlab.jfronny.muscript.data.additional.DataExprMapper;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.parser.Parser;
import io.gitlab.jfronny.muscript.runtime.Runtime;
import io.gitlab.jfronny.muscript.serialize.Decompiler;
import io.gitlab.jfronny.respackopts.muscript.ScopeVersion;
import io.gitlab.jfronny.respackopts.util.MetaCache;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.Version;
import net.minecraft.client.MinecraftClient;
import net.minecraft.command.argument.IdentifierArgumentType;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import org.apache.commons.io.IOUtils;
import java.io.*;
import java.nio.file.*;
import java.util.concurrent.CompletableFuture;
import static io.gitlab.jfronny.muscript.ast.context.ExprUtils.asString;
import static io.gitlab.jfronny.muscript.serialize.Decompiler.decompile;
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*;
public class RpoClientCommand {
private static final Version VERSION = FabricLoader.getInstance().getModContainer(Respackopts.ID).orElseThrow().getMetadata().getVersion();
public static void register() {
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> {
Command<FabricClientCommandSource> getVersion = ctx -> {
ctx.getSource().sendFeedback(Text.translatable("respackopts.versionText", VERSION, Respackopts.META_VERSION));
return 1;
};
Command<FabricClientCommandSource> dumpConfig = ctx -> {
MetaCache.forEach((id, branch) -> ctx.getSource().sendFeedback(dump(branch.toString(), branch.packId() + ".txt")));
return 1;
};
Command<FabricClientCommandSource> dumpGlsl = ctx -> {
ctx.getSource().sendFeedback(dump(RespackoptsClient.getShaderImportSource(), "frex.glsl"));
return 1;
};
Command<FabricClientCommandSource> dumpScope = ctx -> {
ctx.getSource().sendFeedback(dump(MetaCache.getScope(Respackopts.META_VERSION), "_root.mu"));
MetaCache.forEach((id, branch) -> ctx.getSource().sendFeedback(dump(branch.executionScope().getOverrides(), branch.packId() + ".mu")));
return 1;
};
Command<FabricClientCommandSource> dumpScopeVersioned = ctx -> {
ctx.getSource().sendFeedback(dump(MetaCache.getScope(IntegerArgumentType.getInteger(ctx, "version")), "_root.mu"));
MetaCache.forEach((id, branch) -> ctx.getSource().sendFeedback(dump(branch.executionScope().getOverrides(), branch.packId() + ".mu")));
return 1;
};
Command<FabricClientCommandSource> dumpAsset = ctx -> {
Identifier id = ctx.getArgument("asset", Identifier.class);
ctx.getSource().sendFeedback(dump(() -> MinecraftClient.getInstance().getResourceManager().open(id), id.getNamespace() + "/" + id.getPath()));
return 1;
};
Command<FabricClientCommandSource> execute = ctx -> {
String snippet = StringArgumentType.getString(ctx, "snippet");
try {
int ver = IntegerArgumentType.getInteger(ctx, "version");
String result = Runtime.evaluate(asString(Parser.parse(ScopeVersion.by(ver).muScriptVersion, snippet, "snippet")), MetaCache.getScope(ver));
ctx.getSource().sendFeedback(Text.translatable("respackopts.snippet.success", result));
} catch (LocationalException | Parser.ParseException e) {
Respackopts.LOGGER.error("Could not execute snippet", e);
ctx.getSource().sendError(Text.translatable("respackopts.snippet.failed", e.getMessage()));
}
return 1;
};
Command<FabricClientCommandSource> reload = ctx -> {
MetaCache.clear();
CompletableFuture.allOf(RespackoptsClient.forceReloadResources(), RespackoptsClient.reloadIntegratedServerData())
.thenRun(() -> {
ctx.getSource().sendFeedback(Text.translatable("respackopts.reloadSucceeded"));
}).exceptionally(e -> {
Respackopts.LOGGER.error("Could not reload resources", e);
ctx.getSource().sendError(Text.translatable("respackopts.reloadFailed"));
return null;
});
return 1;
};
dispatcher.register(literal("rpoc").executes(getVersion)
.then(literal("dump").executes(dumpConfig)
.then(literal("config").executes(dumpConfig))
.then(literal("scope").executes(dumpScope).then(argument("version", IntegerArgumentType.integer(1, Respackopts.META_VERSION)).executes(dumpScopeVersioned)))
.then(literal("glsl").executes(dumpGlsl))
.then(literal("asset").then(argument("asset", IdentifierArgumentType.identifier()).executes(dumpAsset))))
.then(literal("execute").then(argument("version", IntegerArgumentType.integer(1, Respackopts.META_VERSION)).then(argument("snippet", StringArgumentType.greedyString()).executes(execute))))
.then(literal("version").executes(getVersion))
.then(literal("reload").executes(reload)));
});
}
private static Text dump(Dynamic dynamic, String fileName) {
return dump(DataExprMapper.map(dynamic), fileName);
}
private static Text dump(Expr expr, String fileName) {
return dump(decompile(expr), fileName);
}
private static final Path dumpPath = FabricLoader.getInstance().getGameDir().resolve("respackopts").toAbsolutePath();
private static Text dump(String text, String fileName) {
return dump(path -> Files.writeString(path, text, StandardOpenOption.CREATE), fileName);
}
private static Text dump(ThrowingSupplier<InputStream, IOException> content, String fileName) {
return dump(path -> {
try (InputStream is = content.get();
OutputStream os = Files.newOutputStream(path, StandardOpenOption.CREATE)) {
IOUtils.copy(is, os);
}
}, fileName);
}
private static Text dump(ThrowingConsumer<Path, IOException> content, String fileName) {
try {
Path filePath = dumpPath.resolve(fileName).normalize();
if (!filePath.startsWith(dumpPath)) throw new IOException("Illegal path");
Files.createDirectories(filePath.getParent());
content.accept(filePath);
return Text.translatable("respackopts.dumpSucceeded", filePath.toAbsolutePath());
}
catch (Throwable e) {
Respackopts.LOGGER.error("Could not dump resource", e);
return Text.translatable("respackopts.dumpFailed");
}
}
}