A LOT OF WORK

This commit is contained in:
TopchetoEU 2024-04-16 10:11:04 +03:00
parent 382122c32f
commit 6d0a4739ac
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
29 changed files with 1834 additions and 127 deletions

View File

@ -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.9.2-beta'
implementation "com.github.TopchetoEU:jscript:${project.jscript_version}"
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

@ -9,6 +9,7 @@ yarn_mappings = 1.19.4+build.2
loader_version = 0.15.3
fabric_version = 0.87.2+1.19.4
modmenu_version = 6.3.1
jscript_version = 0.9.41-beta
# Project properties

View File

@ -3,7 +3,10 @@
"minVersion": "0.8",
"package": "me.topchetoeu.mcscript.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [],
"mixins": [
"BlockItemMixin",
"ScreenHandlerMixin"
],
"client": [
"ChatScreenMixin",
"MinecraftClientMixin",

View File

@ -11,11 +11,11 @@ public class ClientMessageReceiver implements MessageReceiver {
public final MinecraftClient client = MinecraftClient.getInstance();
@Override
public void sendError(String msg) {
public synchronized void sendError(String msg) {
client.player.sendMessage(Text.literal("").append(msg).formatted(Formatting.RED));
}
@Override
public void sendInfo(String msg) {
public synchronized void sendInfo(String msg) {
client.player.sendMessage(Text.of(msg));
}
}

View File

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

View File

@ -0,0 +1,55 @@
package me.topchetoeu.mcscript;
import static net.minecraft.server.command.CommandManager.argument;
import static net.minecraft.server.command.CommandManager.literal;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.minecraft.command.EntitySelector;
import net.minecraft.command.argument.EntityArgumentType;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LightningEntity;
import net.minecraft.entity.player.PlayerEntity;
public class Main implements ModInitializer {
private void smite(PlayerEntity player) {
var world = player.getWorld();
var bolt = new LightningEntity(EntityType.LIGHTNING_BOLT, world);
bolt.setPosition(player.getPos());
world.spawnEntity(bolt);
}
private void feed(PlayerEntity player) {
player.getHungerManager().setSaturationLevel(20);
player.getHungerManager().setFoodLevel(20);
}
@Override public void onInitialize() {
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(
literal("smite").then(argument("player", EntityArgumentType.players())).executes(context -> {
var selector = context.getArgument("player", EntitySelector.class);
for (var player : selector.getPlayers(context.getSource())) {
smite(player);
}
return 1;
})
);
dispatcher.register(
literal("feed").then(argument("player", EntityArgumentType.players())).executes(context -> {
var selector = context.getArgument("player", EntitySelector.class);
for (var player : selector.getPlayers(context.getSource())) {
feed(player);
}
return 1;
})
);
});
}
}

View File

@ -1,51 +1,31 @@
package me.topchetoeu.mcscript;
import static net.minecraft.server.command.CommandManager.argument;
import static net.minecraft.server.command.CommandManager.literal;
import java.io.IOException;
import java.net.InetSocketAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import me.topchetoeu.jscript.utils.debug.DebugServer;
import me.topchetoeu.jscript.utils.filesystem.File;
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.event.lifecycle.v1.ClientLifecycleEvents;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.minecraft.command.EntitySelector;
import net.minecraft.command.argument.EntityArgumentType;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LightningEntity;
public class McScript implements ModInitializer, ClientModInitializer {
// private boolean openDev = false;
public class McScript implements ModInitializer {
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(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);
// var fs = new RootFilesystem(PermissionsProvider.get(env));
// fs.protocols.put("std", new STDFilesystem().add("in", inFile).add("out", outFile));
// var perms = new PermissionsManager();
// env.add(PermissionsProvider.KEY, perms);
// env.add(Filesystem.KEY, fs);
// env.global.define(null, "env", true, location);
// return env;
// }
@Override public void onInitialize() {
var mods = new ModManager("mods", "mod-data");
var server = new DebugServer();
server.start(new InetSocketAddress(9229), true);
var mods = new ModManager("mods", "mod-data", server);
try {
mods.load();
}
@ -53,69 +33,24 @@ public class McScript implements ModInitializer, ClientModInitializer {
e.printStackTrace();
}
// 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 engine = new Engine();
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(
literal("smite").then(argument("player", EntityArgumentType.players()).executes(context -> {
var player = context.getArgument("player", EntitySelector.class);
// ServerLifecycleEvents.SERVER_STARTING.register(server -> {
// engine.start();
// 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();
// });
for (var entity : player.getEntities(context.getSource())) {
var world = entity.getWorld();
var bolt = new LightningEntity(EntityType.LIGHTNING_BOLT, world);
bolt.setPosition(entity.getPos());
world.spawnEntity(bolt);
}
@net.fabricmc.api.Environment(EnvType.CLIENT)
@Override public void onInitializeClient() {
// var dev = new DevScreen();
// var engine = new Engine();
ClientLifecycleEvents.CLIENT_STARTED.register((client) -> {
// 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;
// })
// );
// });
return 1;
}))
);
});
// ClientLifecycleEvents.CLIENT_STOPPING.register((client) -> {
// engine.stop();
// });
}
}

View File

@ -0,0 +1,131 @@
package me.topchetoeu.mcscript;
import java.util.Queue;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import me.topchetoeu.jscript.common.ResultRunnable;
import me.topchetoeu.jscript.common.events.DataNotifier;
import me.topchetoeu.jscript.lib.PromiseLib;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.exceptions.InterruptException;
public class MessageQueue {
private static final WeakHashMap<Thread, MessageQueue> queues = new WeakHashMap<>();
private Queue<Runnable> tasks = new ConcurrentLinkedDeque<>();
private final Thread thread;
private boolean awaiting = false;
private boolean interrupted = false;
public void runQueue() {
synchronized (tasks) {
while (!tasks.isEmpty()) {
var task = tasks.poll();
task.run();
}
}
}
public PromiseLib enqueuePromise(Runnable runnable) {
var res = new PromiseLib();
if (Thread.currentThread() == thread) {
runnable.run();
res.fulfill(null, null);
return res;
}
synchronized (tasks) {
tasks.add(() -> {
try {
runnable.run();
res.fulfill(null, null);
}
catch (EngineException e) {
res.reject(null, e);
}
catch (Exception e) {
res.reject(null, new EngineException(e));
}
});
}
return res;
}
public <T> T enqueueSync(ResultRunnable<T> runnable) {
if (Thread.currentThread() == thread) {
return runnable.run();
}
var notif = new DataNotifier<T>();
synchronized (tasks) {
tasks.add(() -> {
try {
notif.next(runnable.run());
}
catch (RuntimeException e){
notif.error(e);
}
notif.next(null);
});
}
synchronized (this) {
if (awaiting) {
interrupted = true;
thread.interrupt();
}
}
return notif.await();
}
public void enqueueSync(Runnable runnable) {
enqueueSync(() -> {
runnable.run();
return null;
});
}
public <T> T await(DataNotifier<T> notif) {
if (awaiting) {
System.out.println("Tried to double-await, ignoring...");
return null;
}
synchronized (Thread.currentThread()) {
runQueue();
while (true) {
try {
awaiting = true;
return (T)notif.await();
}
catch (InterruptException e) {
if (!interrupted) throw new InterruptException();
Thread.interrupted();
synchronized (this) { interrupted = false; }
runQueue();
}
finally {
awaiting = interrupted = false;
Thread.interrupted();
runQueue();
}
}
}
}
private MessageQueue(Thread thread) {
this.thread = thread;
}
public static MessageQueue get() {
return get(Thread.currentThread());
}
public static MessageQueue get(Thread thread) {
queues.putIfAbsent(thread, new MessageQueue(thread));
return queues.get(thread);
}
}

View File

@ -0,0 +1,218 @@
package me.topchetoeu.mcscript.core;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.ConvertHint;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.interop.Arguments;
import net.minecraft.block.BlockState;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.AbstractNbtList;
import net.minecraft.nbt.AbstractNbtNumber;
import net.minecraft.nbt.NbtByte;
import net.minecraft.nbt.NbtByteArray;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtDouble;
import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtFloat;
import net.minecraft.nbt.NbtInt;
import net.minecraft.nbt.NbtIntArray;
import net.minecraft.nbt.NbtList;
import net.minecraft.nbt.NbtLong;
import net.minecraft.nbt.NbtLongArray;
import net.minecraft.nbt.NbtShort;
import net.minecraft.nbt.NbtString;
import net.minecraft.registry.Registries;
import net.minecraft.state.property.BooleanProperty;
import net.minecraft.state.property.EnumProperty;
import net.minecraft.state.property.IntProperty;
import net.minecraft.state.property.Property;
import net.minecraft.util.Identifier;
@SuppressWarnings("all")
public class Data {
public static NbtCompound toNBT(Extensions ext, ObjectValue obj) {
var res = new NbtCompound();
for (var key : Values.getMembers(ext, obj, true, false)) {
if (!(key instanceof String)) continue;
var skey = (String)key;
String propType = null;
var i = skey.indexOf("$");
if (i >= 0) {
propType = skey.substring(i + 1);
skey = skey.substring(0, i);
}
var val = toNBT(ext, Values.getMember(ext, obj, skey), propType);
if (val != null) res.put(skey, val);
}
return res;
}
public static NbtList toNBT(Extensions ext, ArrayValue arr, String type) {
var res = new NbtList();
for (var el : arr) {
var val = toNBT(ext, el, type);
if (val != null) res.add(val);
}
return res;
}
public static NbtList toNBT(Extensions ext, ArrayValue arr) {
return toNBT(ext, arr, null);
}
public static NbtLongArray toNBTLongArr(Extensions ext, ArrayValue arr) {
var res = new long[arr.size()];
for (var i = 0; i < arr.size(); i++) res[i] = (long)Values.toNumber(ext, arr.get(i));
return new NbtLongArray(res);
}
public static NbtIntArray toNBTIntArr(Extensions ext, ArrayValue arr) {
var res = new int[arr.size()];
for (var i = 0; i < arr.size(); i++) res[i] = (int)Values.toNumber(ext, arr.get(i));
return new NbtIntArray(res);
}
public static NbtByteArray toNBTByteArr(Extensions ext, ArrayValue arr) {
var res = new byte[arr.size()];
for (var i = 0; i < arr.size(); i++) res[i] = (byte)Values.toNumber(ext, arr.get(i));
return new NbtByteArray(res);
}
public static NbtElement toNBT(Extensions ext, Object obj, String type) {
if (type == null) type = "";
if (obj instanceof ArrayValue) {
var arr = (ArrayValue)obj;
switch (type) {
case "l": return toNBTLongArr(ext, arr);
case "i": return toNBTIntArr(ext, arr);
case "b": return toNBTByteArr(ext, arr);
default: return toNBT(ext, arr);
}
}
switch (type) {
case "l": return NbtLong.of((long)Values.toNumber(ext, obj));
case "i": return NbtInt.of((int)Values.toNumber(ext, obj));
case "s": return NbtShort.of((short)Values.toNumber(ext, obj));
case "b": return NbtByte.of((byte)Values.toNumber(ext, obj));
case "f": return NbtFloat.of((float)Values.toNumber(ext, obj));
case "d": return NbtDouble.of((double)Values.toNumber(ext, obj));
}
if (obj instanceof ObjectValue) return toNBT(ext, (ObjectValue)obj);
var prim = Values.toPrimitive(ext, obj, ConvertHint.VALUEOF);
if (prim instanceof Number) return NbtDouble.of(((Number)prim).doubleValue());
if (prim instanceof Boolean) return NbtByte.of((boolean)prim);
if (prim instanceof String) return NbtString.of((String)prim);
return null;
}
public static NbtElement toNBT(Extensions ext, Object obj) {
return toNBT(ext, obj, null);
}
public static NbtElement toNBT(Arguments args, int i) {
return toNBT(args, args.get(i));
}
public static NbtCompound toCompound(Arguments args, int i) {
var val = args.get(i);
if (val instanceof ObjectValue) {
return toNBT(args, (ObjectValue)val);
}
return new NbtCompound();
}
public static ObjectValue toJS(Extensions ext, NbtCompound cmp) {
var res = new ObjectValue();
for (var key : cmp.getKeys()) {
var val = toJS(ext, cmp.get(key));
if (val != null) res.defineProperty(ext, key, val);
}
return res;
}
public static Object toJS(Extensions ext, NbtElement obj) {
if (obj instanceof AbstractNbtNumber) return ((AbstractNbtNumber)obj).doubleValue();
if (obj instanceof NbtString) return ((NbtString)obj).asString();
if (obj instanceof AbstractNbtList) {
var arr = (AbstractNbtList<?>)obj;
var res = new ArrayValue(arr.size());
for (var el : arr) {
var val = toJS(ext, el);
if (val != null) res.set(ext, res.size(), val);
}
return res;
}
if (obj instanceof NbtCompound) return toJS(ext, (NbtCompound)obj);
return null;
}
public static NbtCompound toNBT(BlockState state) {
var res = new NbtCompound();
res.put("id", NbtString.of(Registries.BLOCK.getId(state.getBlock()).toString()));
for (var prop : state.getProperties()) {
if (prop instanceof EnumProperty) res.put(prop.getName(), NbtString.of(state.get(prop).toString()));
if (prop instanceof IntProperty) res.put(prop.getName(), NbtInt.of((int)state.get((IntProperty)prop)));
if (prop instanceof BooleanProperty) res.put(prop.getName(), NbtByte.of((boolean)state.get((BooleanProperty)prop)));
}
return res;
}
public static BlockState toBlockState(NbtCompound cmp) {
var block = Registries.BLOCK.get(new Identifier(cmp.getString("id")));
var mgr = block.getStateManager();
var state = mgr.getDefaultState();
for (var key : cmp.getKeys()) {
var prop = (Property<?>)mgr.getProperty(key);
if (prop == null) continue;
if (prop instanceof IntProperty) state = state.with((IntProperty)prop, cmp.getInt(key));
if (prop instanceof EnumProperty<?>) state = state.with((Property)prop, (Comparable)prop.parse(cmp.getString(key)).get());
if (prop instanceof BooleanProperty) state = state.with((BooleanProperty)prop, cmp.getBoolean(key));
}
return state;
}
public static NbtCompound toNBT(ItemStack stack) {
var res = new NbtCompound();
res.put("id", NbtString.of(Registries.ITEM.getId(stack.getItem()).toString()));
res.put("count", NbtInt.of(stack.getCount()));
if (stack.hasNbt()) res.copyFrom(stack.getNbt());
return res;
}
public static ItemStack toItemStack(NbtCompound cmp) {
if (cmp == null) return null;
var item = Registries.ITEM.get(new Identifier(cmp.getString("id")));
var stack = new ItemStack(item, cmp.getInt("count"));
var nbt = cmp.copy();
nbt.remove("id");
nbt.remove("count");
stack.setNbt(nbt);
return stack;
}
}

View File

@ -1,8 +1,8 @@
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.runtime.Engine;
import me.topchetoeu.jscript.runtime.Environment;
import me.topchetoeu.jscript.runtime.EventLoop;
import me.topchetoeu.jscript.utils.filesystem.RootFilesystem;
public class Mod {

View File

@ -6,17 +6,29 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.ZipFile;
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.runtime.Compiler;
import me.topchetoeu.jscript.runtime.Engine;
import me.topchetoeu.jscript.runtime.Environment;
import me.topchetoeu.jscript.runtime.EventLoop;
import me.topchetoeu.jscript.runtime.debug.DebugContext;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.lib.Internals;
import me.topchetoeu.jscript.utils.JSCompiler;
import me.topchetoeu.jscript.utils.debug.DebugServer;
import me.topchetoeu.jscript.utils.debug.SimpleDebugger;
import me.topchetoeu.jscript.utils.filesystem.ActionType;
import me.topchetoeu.jscript.utils.filesystem.EntryType;
import me.topchetoeu.jscript.utils.filesystem.ErrorReason;
import me.topchetoeu.jscript.utils.filesystem.File;
import me.topchetoeu.jscript.utils.filesystem.FileStat;
import me.topchetoeu.jscript.utils.filesystem.Filesystem;
import me.topchetoeu.jscript.utils.filesystem.FilesystemException;
import me.topchetoeu.jscript.utils.filesystem.Mode;
import me.topchetoeu.jscript.utils.filesystem.PhysicalFilesystem;
import me.topchetoeu.jscript.utils.filesystem.RootFilesystem;
import me.topchetoeu.jscript.utils.filesystem.STDFilesystem;
@ -24,32 +36,109 @@ 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;
import me.topchetoeu.mcscript.lib.MCInternals;
public class ModManager {
public final String codeFolder, dataFolder;
public final List<Mod> mods = new ArrayList<>();
public final DebugServer debugServer;
private void loadMod(String folder) throws IOException {
var filename = Path.of(folder, "manifest.json");
if (!filename.toFile().exists() || !filename.toFile().isFile()) return;
private Filesystem zipFs(Path path) {
var zipPath = new Path[] { path };
var rawManifest = new String(Files.readAllBytes(filename));
var manifest = JSON.parse(Filename.fromFile(filename.toFile()), rawManifest).map();
return new Filesystem() {
@Override public void close() {
zipPath[0] = null;
}
@Override public File open(String path, Mode mode) {
path = normalize(path).substring(1);
try {
if (zipPath[0] == null) throw new FilesystemException(ErrorReason.CLOSED, "Filesystem closed.");
var file = new ZipFile(zipPath[0].toFile());
var entry = file.getEntry(path);
if (entry == null) {
file.close();
throw new FilesystemException(ErrorReason.DOESNT_EXIST);
}
if (mode.writable) {
file.close();
throw new FilesystemException(ErrorReason.NO_PERMISSION, "Zip filesystem is read-only.")
.setEntry(entry.isDirectory() ? EntryType.FOLDER : EntryType.FILE);
}
var res = File.ofStream(file.getInputStream(entry));
return new File() {
@Override public int read(byte[] buff) { return res.read(buff); }
@Override public long seek(long offset, int pos) { return res.seek(offset, pos); }
@Override public void write(byte[] buff) { res.write(buff); }
@Override public boolean close() {
if (res.close()) {
try { file.close(); }
catch (IOException e) { }
return true;
}
return false;
}
};
}
catch (IOException e) {
throw new FilesystemException(ErrorReason.UNKNOWN, e.getMessage()).setPath(path).setAction(ActionType.OPEN);
}
catch (FilesystemException e) {
throw e.setPath(path).setAction(ActionType.OPEN);
}
}
@Override public FileStat stat(String path) {
path = normalize(path);
try {
if (zipPath[0] == null) throw new FilesystemException(ErrorReason.CLOSED, "Filesystem closed.");
var file = new ZipFile(zipPath[0].toFile());
var entry = file.getEntry(path);
file.close();
if (entry == null) return new FileStat(Mode.NONE, EntryType.NONE);
else if (entry.isDirectory()) return new FileStat(Mode.READ, EntryType.FOLDER);
else return new FileStat(Mode.READ, EntryType.FILE);
}
catch (IOException e) {
throw new FilesystemException(ErrorReason.UNKNOWN, e.getMessage()).setPath(path).setAction(ActionType.STAT);
}
catch (FilesystemException e) {
throw e.setPath(path).setAction(ActionType.STAT);
}
}
};
}
private void loadFileMod(Filesystem codeFs) throws IOException {
var file = codeFs.open("manifest.json", Mode.READ);
var manifest = JSON.parse(new Filename("code", "manifest.json"), file.readToString()).map();
file.close();
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)));
file = codeFs.open(main, Mode.READ);
var mainSrc = file.readToString();
file.close();
var env = new Environment();
var loop = new Engine();
Files.createDirectories(Path.of(dataFolder.toString(), name));
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));
fs.protocols.put("code", codeFs);
var perms = new PermissionsManager();
perms.add("jscript.file.read:**");
@ -61,13 +150,24 @@ public class ModManager {
var modules = new RootModuleRepo();
modules.repos.put("file", ModuleRepo.ofFilesystem(fs.protocols.get("code")));
var debug = new DebugContext();
debugServer.targets.put(name, (socket, req) -> new SimpleDebugger(socket).attach(debug));
Internals.apply(env);
MCInternals.apply(env);
env.add(EventLoop.KEY, loop);
env.add(Filesystem.KEY, fs);
env.add(PermissionsProvider.KEY, perms);
env.add(Compiler.KEY, new JSCompiler(env));
env.add(DebugContext.KEY, debug);
loop.pushMsg(false, env, new Filename("code", main), mainSrc, null);
new Thread(() -> {
try {
loop.pushMsg(false, env, new Filename("code", main), mainSrc, null).await();
}
catch (EngineException e) { Values.printError(e, "in mod initializer"); }
}, "Awaiter").start();
var mod = new Mod(name, author, version, loop, env, fs);
mods.add(mod);
@ -75,19 +175,17 @@ public class ModManager {
public void load() throws IOException {
for (var file : Files.list(Path.of(codeFolder)).collect(Collectors.toList())) {
if (!Files.isDirectory(file)) continue;
loadMod(file.toString());
if (Files.isDirectory(file)) {
loadFileMod(new PhysicalFilesystem(file.toString()));
}
else {
loadFileMod(zipFs(file));
}
}
}
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);
mod.fs.protocols.put("std", new STDFilesystem(in, out, err));
}
}
public void start() {
@ -96,8 +194,9 @@ public class ModManager {
}
}
public ModManager(String folder, String dataFolder) {
public ModManager(String folder, String dataFolder, DebugServer server) {
this.codeFolder = folder;
this.dataFolder = dataFolder;
this.debugServer = server;
}
}

View File

@ -9,9 +9,13 @@ public interface ChatMessageCallback {
public boolean cancelled;
}
void execute(ChatArgs args);
boolean execute(ChatArgs args);
public static final Event<ChatMessageCallback> EVENT = EventFactory.createArrayBacked(ChatMessageCallback.class, arr -> args -> {
for (var el : arr) el.execute(args);
for (var el : arr) {
if (!el.execute(args)) return false;
}
return true;
});
}

View File

@ -0,0 +1,19 @@
package me.topchetoeu.mcscript.events;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.block.BlockState;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.math.BlockPos;
public interface PlayerBlockPlaceEvent {
boolean blockPlaced(ServerPlayerEntity player, BlockPos pos, BlockState state);
public static final Event<PlayerBlockPlaceEvent> EVENT = EventFactory.createArrayBacked(PlayerBlockPlaceEvent.class, arr -> (player, pos, state) -> {
for (var el : arr) {
if (!el.blockPlaced(player, pos, state)) return false;
}
return true;
});
}

View File

@ -0,0 +1,31 @@
package me.topchetoeu.mcscript.events;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.server.network.ServerPlayerEntity;
public class ScreenHandlerEvents {
public static interface SlotClickHandler {
boolean slotClicked(ScreenHandler handler, int slotIndex, int button, SlotActionType actionType, ServerPlayerEntity player);
}
public static interface ScreenCloseHandler {
boolean screenClosed(ScreenHandler handler, ServerPlayerEntity player);
}
public static final Event<SlotClickHandler> SLOT_CLICKED = EventFactory.createArrayBacked(SlotClickHandler.class, arr -> (handler, slotIndex, button, actionType, player) -> {
for (var el : arr) {
if (!el.slotClicked(handler, slotIndex, button, actionType, player)) return false;
}
return true;
});
public static final Event<ScreenCloseHandler> SCREEN_CLOSE = EventFactory.createArrayBacked(ScreenCloseHandler.class, arr -> (handler, player) -> {
for (var el : arr) {
if (!el.screenClosed(handler, player)) return false;
}
return true;
});
}

View File

@ -0,0 +1,66 @@
package me.topchetoeu.mcscript.lib;
import me.topchetoeu.jscript.runtime.Environment;
import me.topchetoeu.jscript.runtime.scope.GlobalScope;
import me.topchetoeu.jscript.runtime.values.NativeFunction;
import me.topchetoeu.jscript.utils.interop.ExposeField;
import me.topchetoeu.jscript.utils.interop.ExposeTarget;
import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider;
import me.topchetoeu.mcscript.lib.common.entities.EntityLib;
import me.topchetoeu.mcscript.lib.common.entities.LivingEntityLib;
import me.topchetoeu.mcscript.lib.common.entities.PlayerLib;
import me.topchetoeu.mcscript.lib.server.ServerLib;
import me.topchetoeu.mcscript.lib.server.entities.ServerPlayerLib;
import me.topchetoeu.mcscript.lib.server.inventory.InventoryLib;
import me.topchetoeu.mcscript.lib.server.inventory.InventoryScreenLib;
import me.topchetoeu.mcscript.lib.server.world.ServerWorldLib;
import me.topchetoeu.mcscript.lib.utils.EventLib;
import me.topchetoeu.mcscript.lib.utils.LocationLib;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.Inventory;
import net.minecraft.inventory.SimpleInventory;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
public class MCInternals {
@ExposeField(target = ExposeTarget.STATIC)
public static final EventLib __serverLoad = new EventLib();
static {
ServerLifecycleEvents.SERVER_STARTED.register(server -> {
__serverLoad.invoke(server);
});
}
public static void apply(Environment env) {
var wp = NativeWrapperProvider.get(env);
var glob = new GlobalScope(wp.getNamespace(MCInternals.class));
glob.obj.setPrototype(env, GlobalScope.get(env).obj);
env.remove(GlobalScope.KEY);
env.add(GlobalScope.KEY, glob);
wp.set(Entity.class, EntityLib.class);
wp.set(LivingEntity.class, LivingEntityLib.class);
wp.set(PlayerEntity.class, PlayerLib.class);
wp.set(ServerPlayerEntity.class, ServerPlayerLib.class);
wp.set(MinecraftServer.class, ServerLib.class);
wp.set(ServerWorld.class, ServerWorldLib.class);
wp.set(Inventory.class, InventoryLib.class);
wp.set(ScreenHandler.class, InventoryScreenLib.class);
glob.define(env, false, wp.getConstr(MinecraftServer.class));
glob.define(env, false, wp.getConstr(LocationLib.class));
glob.define(env, false, wp.getConstr(Entity.class));
glob.define(env, false, wp.getConstr(LivingEntity.class));
glob.define(env, false, wp.getConstr(PlayerEntity.class));
glob.define(env, false, wp.getConstr(ServerPlayerEntity.class));
glob.define(env, false, new NativeFunction("Inventory", args -> {
return new SimpleInventory(args.getInt(0));
}));
}
}

View File

@ -0,0 +1,89 @@
package me.topchetoeu.mcscript.lib.common.entities;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeType;
import me.topchetoeu.jscript.utils.interop.WrapperName;
import me.topchetoeu.mcscript.core.Data;
import me.topchetoeu.mcscript.lib.server.ServerLib;
import me.topchetoeu.mcscript.lib.utils.LocationLib;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import net.minecraft.world.World;
@WrapperName("Entity")
public class EntityLib {
@Expose(type = ExposeType.GETTER)
public static World __world(Arguments args) {
return args.self(Entity.class).getWorld();
}
@Expose(type = ExposeType.GETTER)
public static String __name(Arguments args) {
var self = args.self(Entity.class);
return self.getDisplayName().getString();
}
@Expose(type = ExposeType.SETTER, value = "name")
public static void __setName(Arguments args) {
var self = args.self(Entity.class);
ServerLib.runSync(self.getServer(), () -> {
self.setCustomName(Text.of(args.getString(0)));
});
}
@Expose(type = ExposeType.GETTER)
public static LocationLib __location(Arguments args) {
var self = args.self(Entity.class);
return new LocationLib(self.getX(), self.getY(), self.getZ());
}
@Expose(type = ExposeType.SETTER, value = "location")
public static void __setLocation(Arguments args) {
var self = args.self(Entity.class);
ServerLib.runSync(self.getServer(), () -> {
var pos = new LocationLib(args);
self.refreshPositionAfterTeleport(pos.x, pos.y, pos.z);
});
}
@Expose(type = ExposeType.GETTER)
public static String __uuid(Arguments args) {
return args.self(ServerPlayerEntity.class).getUuidAsString();
}
@Expose public static void __clearName(Arguments args) {
var self = args.self(Entity.class);
ServerLib.runSync(self.getServer(), () -> {
self.setCustomName(null);
});
}
@Expose(type = ExposeType.GETTER)
public static ObjectValue __nbt(Arguments args) {
var self = args.self(Entity.class);
return ServerLib.runSync(self.getServer(), () -> {
var res = new NbtCompound();
self.writeNbt(res);
return Data.toJS(args, res);
});
}
@Expose(type = ExposeType.SETTER, value = "nbt")
public static void __setNbt(Arguments args) {
var self = args.self(Entity.class);
ServerLib.runSync(self.getServer(), () -> {
var nbt = Data.toNBT(args, 0);
self.readNbt((NbtCompound)nbt);
});
}
@Expose public static void __discard(Arguments args) {
var self = args.self(Entity.class);
ServerLib.runSync(self.getServer(), () -> {
self.discard();
});
}
}

View File

@ -0,0 +1,27 @@
package me.topchetoeu.mcscript.lib.common.entities;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeType;
import me.topchetoeu.jscript.utils.interop.WrapperName;
import me.topchetoeu.mcscript.lib.server.ServerLib;
import net.minecraft.entity.LivingEntity;
@WrapperName("LivingEntity")
public class LivingEntityLib {
@Expose(type = ExposeType.GETTER)
public static double __health(Arguments args) {
return args.self(LivingEntity.class).getHealth();
}
@Expose(type = ExposeType.SETTER, value = "health")
public static void __setHealth(Arguments args) {
var self = args.self(LivingEntity.class);
ServerLib.runSync(self.getServer(), () -> {
self.setHealth(args.getFloat(0));
});
}
@Expose(type = ExposeType.GETTER)
public static double __maxHealth(Arguments args) {
return args.self(LivingEntity.class).getMaxHealth();
}
}

View File

@ -0,0 +1,14 @@
package me.topchetoeu.mcscript.lib.common.entities;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.WrapperName;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.text.Text;
@WrapperName("Player")
public class PlayerLib {
@Expose public static void __sendMessage(Arguments args) {
args.self(PlayerEntity.class).sendMessage(Text.of(args.getString(0)));
}
}

View File

@ -0,0 +1,326 @@
package me.topchetoeu.mcscript.lib.server;
import static net.minecraft.server.command.CommandManager.argument;
import static net.minecraft.server.command.CommandManager.literal;
import java.util.Map;
import java.util.WeakHashMap;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.arguments.StringArgumentType;
import me.topchetoeu.jscript.common.ResultRunnable;
import me.topchetoeu.jscript.common.events.DataNotifier;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.EventLoop;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.NativeFunction;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeField;
import me.topchetoeu.jscript.utils.interop.ExposeTarget;
import me.topchetoeu.jscript.utils.interop.ExposeType;
import me.topchetoeu.jscript.utils.interop.WrapperName;
import me.topchetoeu.mcscript.MessageQueue;
import me.topchetoeu.mcscript.events.PlayerBlockPlaceEvent;
import me.topchetoeu.mcscript.events.ScreenHandlerEvents;
import me.topchetoeu.mcscript.lib.utils.EventLib;
import me.topchetoeu.mcscript.lib.utils.LocationLib;
import net.fabricmc.fabric.api.entity.event.v1.ServerEntityWorldChangeEvents;
import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.minecraft.entity.Entity;
import net.minecraft.registry.Registries;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.CommandOutput;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Vec2f;
import net.minecraft.util.math.Vec3d;
@WrapperName("Server")
@SuppressWarnings("resource") // for crying outloud
public class ServerLib {
public static class EventCtx {
@ExposeField public final EventLib blockPlace = new EventLib();
@ExposeField public final EventLib blockBreak = new EventLib();
@ExposeField public final EventLib tickStart = new EventLib();
@ExposeField public final EventLib tickEnd = new EventLib();
@ExposeField public final EventLib playerJoin = new EventLib();
@ExposeField public final EventLib playerLeave = new EventLib();
@ExposeField public final EventLib playerChangeWorld = new EventLib();
@ExposeField public final EventLib entityDamage = new EventLib();
@ExposeField public final EventLib inventoryScreenClicked = new EventLib();
@ExposeField public final EventLib inventoryScreenClosed = new EventLib();
}
private static final WeakHashMap<MinecraftServer, EventCtx> ctxs = new WeakHashMap<>();
public static EventCtx events(MinecraftServer server) {
ctxs.putIfAbsent(server, new EventCtx());
return ctxs.get(server);
}
public static MessageQueue queue(MinecraftServer server) {
return MessageQueue.get(server.getThread());
}
public static MinecraftServer get(Entity obj) {
return obj.getServer();
}
public static <T> T runSync(MinecraftServer server, ResultRunnable<T> runnable) {
var res = new DataNotifier<T>();
queue(server).enqueueSync(() -> {
try { res.next(runnable.run()); }
catch (RuntimeException e) { res.error(e); }
});
return res.await();
}
public static void runSync(MinecraftServer server, Runnable runnable) {
runSync(server, () -> {
runnable.run();
return null;
});
}
@Expose(type = ExposeType.GETTER)
public static EventLib __blockBreak(Arguments args) {
return events(args.self(MinecraftServer.class)).blockBreak;
}
@Expose(type = ExposeType.GETTER)
public static EventLib __blockPlace(Arguments args) {
return events(args.self(MinecraftServer.class)).blockPlace;
}
@Expose(type = ExposeType.GETTER)
public static EventLib __tickStart(Arguments args) {
return events(args.self(MinecraftServer.class)).tickStart;
}
@Expose(type = ExposeType.GETTER)
public static EventLib __tickEnd(Arguments args) {
return events(args.self(MinecraftServer.class)).tickEnd;
}
@Expose(type = ExposeType.GETTER)
public static EventLib __playerJoin(Arguments args) {
return events(args.self(MinecraftServer.class)).playerJoin;
}
@Expose(type = ExposeType.GETTER)
public static EventLib __playerLeave(Arguments args) {
return events(args.self(MinecraftServer.class)).playerLeave;
}
@Expose(type = ExposeType.GETTER)
public static EventLib __playerChangeWorld(Arguments args) {
return events(args.self(MinecraftServer.class)).playerChangeWorld;
}
@Expose(type = ExposeType.GETTER)
public static EventLib __entityDamage(Arguments args) {
return events(args.self(MinecraftServer.class)).entityDamage;
}
@Expose(type = ExposeType.GETTER)
public static EventLib __inventoryScreenClicked(Arguments args) {
return events(args.self(MinecraftServer.class)).inventoryScreenClicked;
}
@Expose(type = ExposeType.GETTER)
public static EventLib __inventoryScreenClosed(Arguments args) {
return events(args.self(MinecraftServer.class)).inventoryScreenClosed;
}
@Expose(type = ExposeType.GETTER)
public static ArrayValue __worlds(Arguments args) {
var server = args.self(MinecraftServer.class);
var res = new ArrayValue();
for (var world : server.getWorlds()) {
res.set(args.ctx, res.size(), world);
}
return res;
}
@Expose(type = ExposeType.GETTER)
public static ArrayValue __players(Arguments args) {
var server = args.self(MinecraftServer.class);
var res = new ArrayValue();
for (var player : server.getPlayerManager().getPlayerList()) {
res.set(args.ctx, res.size(), player);
}
return res;
}
@Expose public static void __registerCommand(Arguments args) {
var server = args.self(MinecraftServer.class);
var name = args.convert(0, String.class);
var obj = args.convert(1, ObjectValue.class);
var exec = (Command<ServerCommandSource>)(context) -> {
return queue(server).await(EventLoop.get(args.ctx).pushMsg(() -> {
try {
String cmdArgs;
var ctx = Context.clean(args.ctx);
try {
cmdArgs = context.getArgument("args", String.class);
}
catch (IllegalArgumentException e) {
cmdArgs = "";
}
var res = Values.call(
ctx, Values.getMember(ctx, obj, "execute"), obj,
cmdArgs,
context.getSource().getEntity(),
new NativeFunction(_args -> {
context.getSource().sendMessage(Text.of(_args.convert(0, String.class)));
return null;
}),
new NativeFunction(_args -> {
context.getSource().sendError(Text.of(_args.convert(0, String.class)));
return null;
})
);
return (int)Values.toNumber(ctx, res);
}
catch (EngineException e) {
context.getSource().sendError(Text.of(Values.errorToReadable(e, "")));
return 0;
}
}, false));
};
server.getCommandManager().getDispatcher().register(literal(name).executes(exec).then(
argument("args", StringArgumentType.greedyString()).executes(exec)
));
}
@Expose public static void __sendMessage(Arguments args) {
var server = args.self(MinecraftServer.class);
var text = args.convert(0, String.class);
server.sendMessage(Text.of(text));
}
@Expose public static ObjectValue __cmd(Arguments args) {
var server = args.self(MinecraftServer.class);
var cmd = args.getString(0);
var opts = (ObjectValue)args.getOrDefault(1, new ObjectValue());
var loc = Values.wrapper(Values.getMember(args, opts, "at"), LocationLib.class);
var pitch = Values.toNumber(args.ctx, Values.getMember(args, opts, "pitch"));
var yaw = Values.toNumber(args.ctx, Values.getMember(args, opts, "yaw"));
var entity = Values.wrapper(Values.getMember(args, opts, "as"), Entity.class);
var world = Values.wrapper(Values.getMember(args, opts, "world"), ServerWorld.class);
if (loc == null) loc = new LocationLib(0, 0, 0);
if (world == null) world = server.getWorlds().iterator().next();
var output = new ArrayValue();
var sender = new ServerCommandSource(
new CommandOutput() {
@Override public void sendMessage(Text text) {
output.set(args, output.size(), text.getString());
}
@Override public boolean shouldBroadcastConsoleToOps() { return false; }
@Override public boolean shouldReceiveFeedback() { return true; }
@Override public boolean shouldTrackOutput() { return false; }
},
new Vec3d(loc.x, loc.y, loc.z), new Vec2f((float)pitch, (float)yaw),
world, 4, "js", Text.of("js"),
server, entity
);
var code = server.getCommandManager().executeWithPrefix(sender, cmd);
return new ObjectValue(args, Map.of(
"output", output,
"code", code
));
}
static {
ServerTickEvents.START_SERVER_TICK.register(server -> {
events(server).tickStart.invoke();
});
ServerTickEvents.END_SERVER_TICK.register(server -> {
events(server).tickEnd.invoke();
});
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
events(server).playerJoin.invoke(handler.player);
});
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
events(server).playerLeave.invoke(handler.player);
});
ServerEntityWorldChangeEvents.AFTER_PLAYER_CHANGE_WORLD.register((player, origin, destination) -> {
events(player.server).playerChangeWorld.invoke(player, origin, destination);
});
PlayerBlockPlaceEvent.EVENT.register((player, pos, state) -> {
return events(player.getServer()).blockPlace.invokeCancellable(
LocationLib.of(pos), player, player.getWorld()
);
});
PlayerBlockBreakEvents.BEFORE.register((world, player, pos, state, blockEntity) -> {
if (!(world instanceof ServerWorld)) return true;
return events(world.getServer()).blockBreak.invokeCancellable(
LocationLib.of(pos), player, world
);
});
ServerLivingEntityEvents.ALLOW_DAMAGE.register((entity, damageSource, damageAmount) -> {
var dmgSrc = new ObjectValue();
dmgSrc.defineProperty(null, "damager", damageSource.getAttacker());
dmgSrc.defineProperty(null, "location", LocationLib.of(damageSource.getPosition()));
dmgSrc.defineProperty(null, "type", damageSource.getType().msgId());
return events(entity.getServer()).entityDamage.invokeCancellable(entity, damageAmount, dmgSrc);
});
ScreenHandlerEvents.SCREEN_CLOSE.register((handler, player) -> {
return events(player.getServer()).inventoryScreenClosed.invokeCancellable(
handler, player
);
});
ScreenHandlerEvents.SLOT_CLICKED.register((handler, slotIndex, rawButton, rawActionType, player) -> {
var actType = "";
switch (rawActionType) {
case CLONE: actType = "clone"; break;
case PICKUP: actType = "pickup"; break;
case PICKUP_ALL: actType = "pickupAll"; break;
case QUICK_CRAFT: actType = "quickCraft"; break;
case QUICK_MOVE: actType = "quickMove"; break;
case SWAP: actType = "swap"; break;
case THROW: actType = "throw"; break;
}
return events(player.getServer()).inventoryScreenClicked.invokeCancellable(
handler, player, slotIndex < 0 ? null : slotIndex, rawButton, actType
);
});
// .ALLOW_DAMAGE.register((entity, damageSource, damageAmount) -> {
// var dmgSrc = new ObjectValue();
// dmgSrc.defineProperty(null, "damager", damageSource.getAttacker());
// dmgSrc.defineProperty(null, "location", LocationLib.of(damageSource.getPosition()));
// dmgSrc.defineProperty(null, "type", damageSource.getType().msgId());
// return events(entity.getServer()).entityDamage.invokeCancellable(entity, damageAmount, dmgSrc);
// });
}
@Expose(target = ExposeTarget.STATIC)
public static int __maxStack(Arguments args) {
var item = Registries.ITEM.get(new Identifier(args.getString(0)));
if (item == null) return 0;
else return item.getMaxCount();
}
}

View File

@ -0,0 +1,151 @@
package me.topchetoeu.mcscript.lib.server.entities;
import me.topchetoeu.jscript.common.json.JSON;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeType;
import me.topchetoeu.jscript.utils.interop.WrapperName;
import me.topchetoeu.mcscript.lib.server.ServerLib;
import net.minecraft.inventory.Inventory;
import net.minecraft.network.packet.s2c.play.SubtitleS2CPacket;
import net.minecraft.network.packet.s2c.play.TitleFadeS2CPacket;
import net.minecraft.network.packet.s2c.play.TitleS2CPacket;
import net.minecraft.screen.Generic3x3ContainerScreenHandler;
import net.minecraft.screen.GenericContainerScreenHandler;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.screen.ScreenHandlerFactory;
import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.screen.SimpleNamedScreenHandlerFactory;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import net.minecraft.world.GameMode;
@WrapperName("ServerPlayer")
public class ServerPlayerLib {
@Expose(type = ExposeType.GETTER)
public static String __gamemode(Arguments args) {
switch (args.self(ServerPlayerEntity.class).interactionManager.getGameMode()) {
case SURVIVAL: return "survival";
case CREATIVE: return "creative";
case ADVENTURE: return "adventure";
case SPECTATOR: return "spectator";
default: return "unknonw";
}
}
@Expose(type = ExposeType.SETTER, value = "gamemode")
public static void __setGamemode(Arguments args) {
var self = args.self(ServerPlayerEntity.class);
var gm = args.getString(0);
ServerLib.runSync(self.getServer(), () -> {
// System.out.println("Set %s's gamemode to %s".formatted(self.getName().getString(), gm.toString()));
switch (gm) {
case "survival": self.changeGameMode(GameMode.SURVIVAL); break;
case "creative": self.changeGameMode(GameMode.CREATIVE); break;
case "adventure": self.changeGameMode(GameMode.ADVENTURE); break;
case "spectator": self.changeGameMode(GameMode.SPECTATOR); break;
default: throw EngineException.ofError("Invalid gamemode '%s'.".formatted(gm));
}
});
}
@Expose(type = ExposeType.GETTER)
public static double __foodLevel(Arguments args) {
return args.self(ServerPlayerEntity.class).getHungerManager().getFoodLevel();
}
@Expose(type = ExposeType.SETTER, value = "foodLevel")
public static void __setFoodLevel(Arguments args) {
var self = args.self(ServerPlayerEntity.class);
ServerLib.runSync(self.getServer(), () -> {
args.self(ServerPlayerEntity.class).getHungerManager().setFoodLevel(args.getInt(0));
});
}
@Expose(type = ExposeType.GETTER)
public static double __saturation(Arguments args) {
return args.self(ServerPlayerEntity.class).getHungerManager().getSaturationLevel();
}
@Expose(type = ExposeType.SETTER, value = "saturation")
public static void __setSaturation(Arguments args) {
var self = args.self(ServerPlayerEntity.class);
self.getInventory();
ServerLib.runSync(self.getServer(), () -> {
args.self(ServerPlayerEntity.class).getHungerManager().setSaturationLevel(args.getFloat(0));
});
}
@Expose public static void __sendTitle(Arguments args) {
var self = args.self(ServerPlayerEntity.class);
var conf = JSON.fromJs(args.ctx, args.convert(0, ObjectValue.class)).map();
var title = conf.string("title", "");
var subtitle = conf.string("subtitle", "");
var fadeIn = conf.number("fadeIn", 0);
var fadeOut = conf.number("fadeOut", 0);
var duration = conf.number("duration", 1);
self.networkHandler.sendPacket(new TitleFadeS2CPacket((int)(fadeIn * 20), (int)(duration * 20), (int)(fadeOut * 20)));
self.networkHandler.sendPacket(new SubtitleS2CPacket(Text.of(subtitle)));
self.networkHandler.sendPacket(new TitleS2CPacket(Text.of(title)));
}
@Expose(type = ExposeType.GETTER)
public static Inventory __inventory(Arguments args) {
var self = args.self(ServerPlayerEntity.class);
return self.getInventory();
}
@Expose(type = ExposeType.GETTER)
public static ScreenHandler __screen(Arguments args) {
var self = args.self(ServerPlayerEntity.class);
return self.currentScreenHandler;
}
@Expose public static ScreenHandler __openInventory(Arguments args) {
var self = args.self(ServerPlayerEntity.class);
var name = args.getString(0);
var type = args.getString(1);
var inv = args.convert(2, Inventory.class);
ScreenHandlerFactory factory;
switch (type) {
case "3x3":
factory = (i, pinv, player) -> new Generic3x3ContainerScreenHandler(i, pinv, inv);
break;
case "9x1":
factory = (i, pinv, player) -> new GenericContainerScreenHandler(ScreenHandlerType.GENERIC_9X1, i, pinv, inv, 1);
break;
case "9x2":
factory = (i, pinv, player) -> new GenericContainerScreenHandler(ScreenHandlerType.GENERIC_9X2, i, pinv, inv, 2);
break;
case "9x3":
factory = (i, pinv, player) -> new GenericContainerScreenHandler(ScreenHandlerType.GENERIC_9X3, i, pinv, inv, 3);
break;
case "9x4":
factory = (i, pinv, player) -> new GenericContainerScreenHandler(ScreenHandlerType.GENERIC_9X4, i, pinv, inv, 4);
break;
case "9x5":
factory = (i, pinv, player) -> new GenericContainerScreenHandler(ScreenHandlerType.GENERIC_9X5, i, pinv, inv, 5);
break;
case "9x6":
default:
factory = (i, pinv, player) -> new GenericContainerScreenHandler(ScreenHandlerType.GENERIC_9X6, i, pinv, inv, 6);
break;
}
self.openHandledScreen(new SimpleNamedScreenHandlerFactory(factory, Text.of(name)));
return self.currentScreenHandler;
}
@Expose public static void __closeInventory(Arguments args) {
var self = args.self(ServerPlayerEntity.class);
ServerLib.runSync(self.getServer(), () -> {
self.closeHandledScreen();
});
}
}

View File

@ -0,0 +1,96 @@
package me.topchetoeu.mcscript.lib.server.inventory;
import java.util.Iterator;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeType;
import me.topchetoeu.jscript.utils.interop.WrapperName;
import me.topchetoeu.mcscript.core.Data;
import net.minecraft.inventory.Inventory;
import net.minecraft.inventory.SimpleInventory;
import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound;
@WrapperName("Inventory")
public class InventoryLib {
@Expose(type = ExposeType.GETTER)
public static int __size(Arguments args) {
var self = args.self(Inventory.class);
return self.size();
}
@Expose public static ObjectValue __get(Arguments args) {
var self = args.self(Inventory.class);
var i = args.getInt(0);
return Data.toJS(args, Data.toNBT(self.getStack(i)));
}
@Expose public static void __set(Arguments args) {
var self = args.self(Inventory.class);
var i = args.getInt(0);
var item = Data.toItemStack((NbtCompound)Data.toNBT(args, 1));
if (item != null && item.getItem() != Items.AIR) {
self.setStack(i, item);
return;
}
self.removeStack(i);
}
@Expose public static void __clear(Arguments args) {
var self = args.self(Inventory.class);
self.clear();
}
@Expose public static Inventory __clone(Arguments args) {
var self = args.self(Inventory.class);
var res = new SimpleInventory(self.size());
for (var i = 0; i < res.size(); i++) {
res.setStack(i, self.getStack(i));
}
return res;
}
@Expose public static void __copyFrom(Arguments args) {
var self = args.self(Inventory.class);
var from = args.convert(0, Inventory.class);
for (var i = 0; i < self.size() && i < from.size(); i++) {
self.setStack(i, from.getStack(i));
}
}
@Expose("@@Symbol.iterator")
public static ObjectValue __iterator(Arguments args) {
var self = args.self(Inventory.class);
return Values.toJSIterator(args, new Iterator<>() {
private int i = 0;
@Override public boolean hasNext() {
while (i < self.size()) {
var item = self.getStack(i);
if (item != null && item.getItem() != Items.AIR) return true;
i++;
}
return false;
}
@Override public Object next() {
if (i >= self.size()) return null;
while (i < self.size()) {
var item = self.getStack(i++);
if (item != null && item.getItem() != Items.AIR) return Data.toJS(args, Data.toNBT(item));
}
return null;
}
});
}
}

View File

@ -0,0 +1,31 @@
package me.topchetoeu.mcscript.lib.server.inventory;
import java.util.HashSet;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.WrapperName;
import net.minecraft.inventory.Inventory;
import net.minecraft.screen.ScreenHandler;
// I couldn't have come up with a shittier way to do inventories if my life depended on it
// Bravo, Mojang, Bravo
@WrapperName("InventoryScreen")
public class InventoryScreenLib {
@Expose public static int __id(Arguments args) {
return args.self(ScreenHandler.class).syncId;
}
@Expose public static ArrayValue __inventories(Arguments args) {
var tmp = new HashSet<Inventory>();
var res = new ArrayValue();
for (var slot : args.self(ScreenHandler.class).slots) {
var inv = slot.inventory;
if (tmp.add(inv)) res.set(args, res.size(), inv);
}
return res;
}
}

View File

@ -0,0 +1,109 @@
package me.topchetoeu.mcscript.lib.server.world;
import me.topchetoeu.jscript.common.json.JSON;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.ObjectValue;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeType;
import me.topchetoeu.jscript.utils.interop.WrapperName;
import me.topchetoeu.mcscript.core.Data;
import me.topchetoeu.mcscript.lib.server.ServerLib;
import me.topchetoeu.mcscript.lib.utils.LocationLib;
import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.registry.Registries;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.state.property.Property;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
@WrapperName("ServerWorld")
public class ServerWorldLib {
@Expose(type = ExposeType.GETTER) public static ArrayValue __players(Arguments args) {
var self = args.self(ServerWorld.class);
var res = new ArrayValue();
for (var player : self.getPlayers()) {
res.set(args.ctx, res.size(), player);
}
return res;
}
@Expose public static ObjectValue __getBlock(Arguments args) {
var self = args.self(ServerWorld.class);
var loc = args.convert(0, LocationLib.class);
var desc = new ObjectValue();
ServerLib.runSync(self.getServer(), () -> {
var state = self.getBlockState(new BlockPos((int)loc.x, (int)loc.y, (int)loc.z));
desc.defineProperty(args, "id", Registries.BLOCK.getId(state.getBlock()).toString());
for (var prop : state.getProperties()) {
var raw = state.get(prop);
if (raw instanceof Number || raw instanceof Boolean) desc.defineProperty(args, prop.getName(), raw);
else desc.defineProperty(args, prop.getName(), raw.toString());
}
});
return desc;
}
@SuppressWarnings("all")
@Expose public static void __setBlock(Arguments args) {
var self = args.self(ServerWorld.class);
var loc = args.convert(0, LocationLib.class);
var nbt = JSON.fromJs(args, (ObjectValue)args.getOrDefault(1, new ObjectValue())).map();
var update = args.has(2) ? args.getBoolean(2) : true;
var id = new Identifier(nbt.string("id"));
ServerLib.runSync(self.getServer(), () -> {
var block = Registries.BLOCK.get(id);
if (block == null) throw EngineException.ofError("No block %s.".formatted(id));
var stateMgr = block.getStateManager();
var state = block.getDefaultState();
for (var entry : nbt.entrySet()) {
var prop = stateMgr.getProperty(entry.getKey());
if (prop == null) continue;
var val = prop.parse(entry.getValue().toString());
if (val.isEmpty()) throw EngineException.ofError("Illegal value '%s' provided for property %s.".formatted(entry.getValue(), entry.getKey()));
// I'm so done with java generics
state = state.with((Property)prop, (Comparable)val.get());
}
var pos = new BlockPos((int)loc.x, (int)loc.y, (int)loc.z);
self.setBlockState(pos, state, Block.NOTIFY_LISTENERS | Block.FORCE_STATE);
if (update) {
self.updateNeighbors(pos, state.getBlock());
}
});
}
@Expose public static Entity __summon(Arguments args) {
var self = args.self(ServerWorld.class);
var loc = args.convert(0, LocationLib.class);
var nbt = Data.toCompound(args, 1);
return ServerLib.runSync(self.getServer(), () -> {
var entity = EntityType.loadEntityWithPassengers(nbt, self, val -> {
// val.readNbt(nbt);
val.refreshPositionAndAngles(loc.x, loc.y, loc.z, val.getYaw(), val.getPitch());
return val;
});
if (entity == null) return null;
if (!self.spawnNewEntityAndPassengers(entity)) return null;
return entity;
});
}
}

View File

@ -0,0 +1,80 @@
package me.topchetoeu.mcscript.lib.utils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import me.topchetoeu.jscript.lib.PromiseLib;
import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.EventLoop;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.ArrayValue;
import me.topchetoeu.jscript.runtime.values.FunctionValue;
import me.topchetoeu.jscript.runtime.values.NativeFunction;
import me.topchetoeu.jscript.runtime.values.Values;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.WrapperName;
import me.topchetoeu.mcscript.MessageQueue;
@WrapperName("Event")
public class EventLib {
private HashMap<FunctionValue, Extensions> handles = new HashMap<>();
private HashMap<FunctionValue, Extensions> onceHandles = new HashMap<>();
public void invoke(Object ...args) {
List<Map.Entry<FunctionValue, Extensions>> arr;
synchronized (handles) {
synchronized (onceHandles) {
arr = Stream.concat(handles.entrySet().stream(), onceHandles.entrySet().stream()).collect(Collectors.toList());
onceHandles.clear();
}
}
for (var handle : arr) {
var env = handle.getValue();
var func = handle.getKey();
try {
MessageQueue.get().await(EventLoop.get(env).pushMsg(false, env, func, null, args));
}
catch (EngineException e) { Values.printError(e, "in event handler"); }
}
}
public boolean invokeCancellable(Object ...args) {
var cancelled = new boolean[1];
var newArgs = new Object[args.length + 1];
newArgs[0] = new NativeFunction("cancel", _args -> { cancelled[0] = true; return null; });
System.arraycopy(args, 0, newArgs, 1, args.length);
invoke(newArgs);
return !cancelled[0];
}
@Expose
public void __on(Arguments args) {
var func = args.convert(0, FunctionValue.class);
handles.put(func, Context.clean(args.ctx));
}
@Expose
public void __once(Arguments args) {
var func = args.convert(0, FunctionValue.class);
onceHandles.put(func, Context.clean(args.ctx));
}
@Expose
public PromiseLib __next(Arguments args) {
var promise = new PromiseLib();
onceHandles.put(new NativeFunction(_args -> {
promise.fulfill(_args.ctx, new ArrayValue(_args.ctx, _args.args));
return null;
}), Context.clean(args.ctx));
return promise;
}
}

View File

@ -0,0 +1,163 @@
package me.topchetoeu.mcscript.lib.utils;
import me.topchetoeu.jscript.utils.interop.Arguments;
import me.topchetoeu.jscript.utils.interop.Expose;
import me.topchetoeu.jscript.utils.interop.ExposeConstructor;
import me.topchetoeu.jscript.utils.interop.ExposeType;
import me.topchetoeu.jscript.utils.interop.WrapperName;
import net.minecraft.util.math.Position;
import net.minecraft.util.math.Vec3i;
@WrapperName("Location")
public class LocationLib {
public final double x, y, z;
@Expose(type = ExposeType.GETTER)
public double __x() { return x; }
@Expose(type = ExposeType.GETTER)
public double __y() { return y; }
@Expose(type = ExposeType.GETTER)
public double __z() { return z; }
@Expose public LocationLib __add(Arguments args) {
var resX = x;
var resY = y;
var resZ = z;
int i = 0;
while (i < args.n()) {
if (args.get(i) instanceof Number) {
resX += args.convert(i, Double.class);
resY += args.convert(i + 1, Double.class);
resZ += args.convert(i + 2, Double.class);
i += 3;
}
else {
var val = args.convert(i, LocationLib.class);
resX += val.x;
resY += val.y;
resZ += val.z;
i++;
}
}
return new LocationLib(resX, resY, resZ);
}
@Expose public LocationLib __subtract(Arguments args) {
var resX = x;
var resY = y;
var resZ = z;
int i = 0;
while (i < args.n()) {
if (args.get(i) instanceof Number) {
resX -= args.convert(i, Double.class);
resY -= args.convert(i + 1, Double.class);
resZ -= args.convert(i + 2, Double.class);
i += 3;
}
else {
var val = args.convert(i, LocationLib.class);
resX -= val.x;
resY -= val.y;
resZ -= val.z;
i++;
}
}
return new LocationLib(resX, resY, resZ);
}
@Expose public double __dot(Arguments args) {
var resX = x;
var resY = y;
var resZ = z;
if (args.get(0) instanceof Number) {
resX *= args.convert(0, Double.class);
resY *= args.convert(1, Double.class);
resZ *= args.convert(2, Double.class);
}
else {
var val = args.convert(0, LocationLib.class);
resX *= val.x;
resY *= val.y;
resZ *= val.z;
}
return resX + resY + resZ;
}
@Expose public double __distance(Arguments args) {
var resX = x;
var resY = y;
var resZ = z;
if (args.get(0) instanceof Number) {
resX -= args.convert(0, Double.class);
resY -= args.convert(1, Double.class);
resZ -= args.convert(2, Double.class);
}
else {
var val = args.convert(0, LocationLib.class);
resX -= val.x;
resY -= val.y;
resZ -= val.z;
}
return Math.sqrt(resX * resX + resY * resY + resZ * resZ);
}
@Expose public double __length() {
return Math.sqrt(x * x + y * y + z * z);
}
@Expose public LocationLib __setX(Arguments args) {
if (args.get(0) instanceof Number) return new LocationLib(args.convert(0, Double.class), y, z);
else return new LocationLib(args.convert(0, LocationLib.class).x, y, z);
}
@Expose public LocationLib __setY(Arguments args) {
if (args.get(0) instanceof Number) return new LocationLib(x, args.convert(0, Double.class), z);
else return new LocationLib(x, args.convert(0, LocationLib.class).y, z);
}
@Expose public LocationLib __setZ(Arguments args) {
if (args.get(0) instanceof Number) return new LocationLib(x, y, args.convert(0, Double.class));
else return new LocationLib(x, y, args.convert(0, LocationLib.class).z);
}
@Expose public String __toString(Arguments args) {
return "[%s %s %s]".formatted(x, y, z);
}
public LocationLib(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public LocationLib(Arguments args) {
if (args.get(0) instanceof Number) {
x = args.convert(0, Double.class);
y = args.convert(1, Double.class);
z = args.convert(2, Double.class);
}
else {
var val = args.convert(0, LocationLib.class);
x = val.x;
y = val.y;
z = val.z;
}
}
@ExposeConstructor
public static LocationLib __constructor(Arguments args) {
return new LocationLib(args);
}
public static LocationLib of(Position vec) {
if (vec == null) return null;
return new LocationLib(vec.getX(), vec.getY(), vec.getZ());
}
public static LocationLib of(Vec3i vec) {
if (vec == null) return null;
return new LocationLib(vec.getX(), vec.getY(), vec.getZ());
}
}

View File

@ -0,0 +1,24 @@
package me.topchetoeu.mcscript.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import me.topchetoeu.mcscript.events.PlayerBlockPlaceEvent;
import net.minecraft.block.BlockState;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.server.network.ServerPlayerEntity;
@Mixin(BlockItem.class)
public class BlockItemMixin {
@Inject(method = "place(Lnet/minecraft/item/ItemPlacementContext;Lnet/minecraft/block/BlockState;)Z", at = @At("HEAD"), cancellable = true)
private void onPlace(ItemPlacementContext context, BlockState state, CallbackInfoReturnable<Boolean> cbi) {
if (!(context.getPlayer() instanceof ServerPlayerEntity)) return;
var cancelled = !PlayerBlockPlaceEvent.EVENT.invoker().blockPlaced((ServerPlayerEntity)context.getPlayer(), context.getBlockPos(), state);
if (cancelled) cbi.setReturnValue(false);
}
}

View File

@ -10,6 +10,7 @@ import net.minecraft.client.MinecraftClient;
@Mixin(Keyboard.class)
public class KeyboardMixin {
@Inject(method = "onChar", at = @At("HEAD"))
private void onChar(long window, int codePoint, int modifiers, CallbackInfo cbi) {
var client = MinecraftClient.getInstance();

View File

@ -0,0 +1,34 @@
package me.topchetoeu.mcscript.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import me.topchetoeu.mcscript.events.ScreenHandlerEvents;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.server.network.ServerPlayerEntity;
@Mixin(ScreenHandler.class)
public class ScreenHandlerMixin {
@Inject(method = "onSlotClick", at = @At("HEAD"), cancellable = true)
private void onSlotClick(int slotIndex, int button, SlotActionType actionType, PlayerEntity player, CallbackInfo cbi) {
if (!(player instanceof ServerPlayerEntity sp)) return;
var handle = (ScreenHandler)(Object)this;
var cancelled = !ScreenHandlerEvents.SLOT_CLICKED.invoker().slotClicked(handle, slotIndex, button, actionType, sp);
if (cancelled) cbi.cancel();
}
@Inject(method = "onClosed", at = @At("HEAD"), cancellable = true)
private void onClosed(PlayerEntity player, CallbackInfo cbi) {
if (!(player instanceof ServerPlayerEntity sp)) return;
var handle = (ScreenHandler)(Object)this;
var cancelled = !ScreenHandlerEvents.SCREEN_CLOSE.invoker().screenClosed(handle, sp);
if (cancelled) cbi.cancel();
}
}