A LOT OF WORK
This commit is contained in:
parent
382122c32f
commit
6d0a4739ac
@ -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.9.2-beta'
|
implementation "com.github.TopchetoEU:jscript:${project.jscript_version}"
|
||||||
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}"
|
||||||
|
@ -9,6 +9,7 @@ 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
|
||||||
|
jscript_version = 0.9.41-beta
|
||||||
|
|
||||||
# Project properties
|
# Project properties
|
||||||
|
|
||||||
|
@ -3,7 +3,10 @@
|
|||||||
"minVersion": "0.8",
|
"minVersion": "0.8",
|
||||||
"package": "me.topchetoeu.mcscript.mixin",
|
"package": "me.topchetoeu.mcscript.mixin",
|
||||||
"compatibilityLevel": "JAVA_17",
|
"compatibilityLevel": "JAVA_17",
|
||||||
"mixins": [],
|
"mixins": [
|
||||||
|
"BlockItemMixin",
|
||||||
|
"ScreenHandlerMixin"
|
||||||
|
],
|
||||||
"client": [
|
"client": [
|
||||||
"ChatScreenMixin",
|
"ChatScreenMixin",
|
||||||
"MinecraftClientMixin",
|
"MinecraftClientMixin",
|
||||||
|
@ -11,11 +11,11 @@ public class ClientMessageReceiver implements MessageReceiver {
|
|||||||
public final MinecraftClient client = MinecraftClient.getInstance();
|
public final MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendError(String msg) {
|
public synchronized void sendError(String msg) {
|
||||||
client.player.sendMessage(Text.literal("").append(msg).formatted(Formatting.RED));
|
client.player.sendMessage(Text.literal("").append(msg).formatted(Formatting.RED));
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void sendInfo(String msg) {
|
public synchronized void sendInfo(String msg) {
|
||||||
client.player.sendMessage(Text.of(msg));
|
client.player.sendMessage(Text.of(msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,7 +2,7 @@ package me.topchetoeu.mcscript;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.core.Context;
|
import me.topchetoeu.jscript.runtime.Context;
|
||||||
|
|
||||||
public interface EnvironmentFactory {
|
public interface EnvironmentFactory {
|
||||||
Iterable<String> dependancies();
|
Iterable<String> dependancies();
|
||||||
|
55
src/java/me/topchetoeu/mcscript/Main.java
Normal file
55
src/java/me/topchetoeu/mcscript/Main.java
Normal 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;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,51 +1,31 @@
|
|||||||
package me.topchetoeu.mcscript;
|
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.io.IOException;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.utils.debug.DebugServer;
|
||||||
import me.topchetoeu.jscript.utils.filesystem.File;
|
import me.topchetoeu.jscript.utils.filesystem.File;
|
||||||
import me.topchetoeu.mcscript.core.ModManager;
|
import me.topchetoeu.mcscript.core.ModManager;
|
||||||
import net.fabricmc.api.ClientModInitializer;
|
|
||||||
import net.fabricmc.api.EnvType;
|
|
||||||
import net.fabricmc.api.ModInitializer;
|
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 {
|
public class McScript implements ModInitializer {
|
||||||
// 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) {
|
|
||||||
// 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() {
|
@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 {
|
try {
|
||||||
mods.load();
|
mods.load();
|
||||||
}
|
}
|
||||||
@ -53,69 +33,24 @@ public class McScript implements ModInitializer, ClientModInitializer {
|
|||||||
e.printStackTrace();
|
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.setSTD(null, File.ofLineWriter(LOGGER::info), File.ofLineWriter(LOGGER::info));
|
||||||
mods.start();
|
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 -> {
|
for (var entity : player.getEntities(context.getSource())) {
|
||||||
// engine.start();
|
var world = entity.getWorld();
|
||||||
|
var bolt = new LightningEntity(EntityType.LIGHTNING_BOLT, world);
|
||||||
// var env = createEnv("SERVER", () -> null, value -> {
|
bolt.setPosition(entity.getPos());
|
||||||
// for (var line : value.split("\n", -1)) LOGGER.info(line);
|
world.spawnEntity(bolt);
|
||||||
// });
|
|
||||||
|
|
||||||
// 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)
|
return 1;
|
||||||
@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;
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// ClientLifecycleEvents.CLIENT_STOPPING.register((client) -> {
|
|
||||||
// engine.stop();
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
131
src/java/me/topchetoeu/mcscript/MessageQueue.java
Normal file
131
src/java/me/topchetoeu/mcscript/MessageQueue.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
218
src/java/me/topchetoeu/mcscript/core/Data.java
Normal file
218
src/java/me/topchetoeu/mcscript/core/Data.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
package me.topchetoeu.mcscript.core;
|
package me.topchetoeu.mcscript.core;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.core.Engine;
|
import me.topchetoeu.jscript.runtime.Engine;
|
||||||
import me.topchetoeu.jscript.core.Environment;
|
import me.topchetoeu.jscript.runtime.Environment;
|
||||||
import me.topchetoeu.jscript.core.EventLoop;
|
import me.topchetoeu.jscript.runtime.EventLoop;
|
||||||
import me.topchetoeu.jscript.utils.filesystem.RootFilesystem;
|
import me.topchetoeu.jscript.utils.filesystem.RootFilesystem;
|
||||||
|
|
||||||
public class Mod {
|
public class Mod {
|
||||||
|
@ -6,17 +6,29 @@ import java.nio.file.Path;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Filename;
|
import me.topchetoeu.jscript.common.Filename;
|
||||||
import me.topchetoeu.jscript.common.json.JSON;
|
import me.topchetoeu.jscript.common.json.JSON;
|
||||||
import me.topchetoeu.jscript.core.Compiler;
|
import me.topchetoeu.jscript.runtime.Compiler;
|
||||||
import me.topchetoeu.jscript.core.Engine;
|
import me.topchetoeu.jscript.runtime.Engine;
|
||||||
import me.topchetoeu.jscript.core.Environment;
|
import me.topchetoeu.jscript.runtime.Environment;
|
||||||
import me.topchetoeu.jscript.core.EventLoop;
|
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.lib.Internals;
|
||||||
import me.topchetoeu.jscript.utils.JSCompiler;
|
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.File;
|
||||||
|
import me.topchetoeu.jscript.utils.filesystem.FileStat;
|
||||||
import me.topchetoeu.jscript.utils.filesystem.Filesystem;
|
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.PhysicalFilesystem;
|
||||||
import me.topchetoeu.jscript.utils.filesystem.RootFilesystem;
|
import me.topchetoeu.jscript.utils.filesystem.RootFilesystem;
|
||||||
import me.topchetoeu.jscript.utils.filesystem.STDFilesystem;
|
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.modules.RootModuleRepo;
|
||||||
import me.topchetoeu.jscript.utils.permissions.PermissionsManager;
|
import me.topchetoeu.jscript.utils.permissions.PermissionsManager;
|
||||||
import me.topchetoeu.jscript.utils.permissions.PermissionsProvider;
|
import me.topchetoeu.jscript.utils.permissions.PermissionsProvider;
|
||||||
|
import me.topchetoeu.mcscript.lib.MCInternals;
|
||||||
|
|
||||||
public class ModManager {
|
public class ModManager {
|
||||||
public final String codeFolder, dataFolder;
|
public final String codeFolder, dataFolder;
|
||||||
public final List<Mod> mods = new ArrayList<>();
|
public final List<Mod> mods = new ArrayList<>();
|
||||||
|
public final DebugServer debugServer;
|
||||||
|
|
||||||
private void loadMod(String folder) throws IOException {
|
private Filesystem zipFs(Path path) {
|
||||||
var filename = Path.of(folder, "manifest.json");
|
var zipPath = new Path[] { path };
|
||||||
if (!filename.toFile().exists() || !filename.toFile().isFile()) return;
|
|
||||||
|
|
||||||
var rawManifest = new String(Files.readAllBytes(filename));
|
return new Filesystem() {
|
||||||
var manifest = JSON.parse(Filename.fromFile(filename.toFile()), rawManifest).map();
|
@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 name = manifest.string("name");
|
||||||
var version = manifest.string("version");
|
var version = manifest.string("version");
|
||||||
var author = manifest.string("author");
|
var author = manifest.string("author");
|
||||||
var main = manifest.string("main");
|
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 env = new Environment();
|
||||||
var loop = new Engine();
|
var loop = new Engine();
|
||||||
|
|
||||||
|
Files.createDirectories(Path.of(dataFolder.toString(), name));
|
||||||
|
|
||||||
var fs = new RootFilesystem(PermissionsProvider.get(env));
|
var fs = new RootFilesystem(PermissionsProvider.get(env));
|
||||||
fs.protocols.put("file", new PhysicalFilesystem(Path.of(dataFolder.toString(), name).toString()));
|
fs.protocols.put("file", new PhysicalFilesystem(Path.of(dataFolder.toString(), name).toString()));
|
||||||
fs.protocols.put("code", new PhysicalFilesystem(Path.of(codeFolder.toString(), name).toString()));
|
fs.protocols.put("code", codeFs);
|
||||||
|
|
||||||
Files.createDirectories(Path.of(codeFolder.toString(), name));
|
|
||||||
|
|
||||||
var perms = new PermissionsManager();
|
var perms = new PermissionsManager();
|
||||||
perms.add("jscript.file.read:**");
|
perms.add("jscript.file.read:**");
|
||||||
@ -61,13 +150,24 @@ public class ModManager {
|
|||||||
var modules = new RootModuleRepo();
|
var modules = new RootModuleRepo();
|
||||||
modules.repos.put("file", ModuleRepo.ofFilesystem(fs.protocols.get("code")));
|
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);
|
Internals.apply(env);
|
||||||
|
MCInternals.apply(env);
|
||||||
env.add(EventLoop.KEY, loop);
|
env.add(EventLoop.KEY, loop);
|
||||||
env.add(Filesystem.KEY, fs);
|
env.add(Filesystem.KEY, fs);
|
||||||
env.add(PermissionsProvider.KEY, perms);
|
env.add(PermissionsProvider.KEY, perms);
|
||||||
env.add(Compiler.KEY, new JSCompiler(env));
|
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);
|
var mod = new Mod(name, author, version, loop, env, fs);
|
||||||
mods.add(mod);
|
mods.add(mod);
|
||||||
@ -75,19 +175,17 @@ public class ModManager {
|
|||||||
|
|
||||||
public void load() throws IOException {
|
public void load() throws IOException {
|
||||||
for (var file : Files.list(Path.of(codeFolder)).collect(Collectors.toList())) {
|
for (var file : Files.list(Path.of(codeFolder)).collect(Collectors.toList())) {
|
||||||
if (!Files.isDirectory(file)) continue;
|
if (Files.isDirectory(file)) {
|
||||||
loadMod(file.toString());
|
loadFileMod(new PhysicalFilesystem(file.toString()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
loadFileMod(zipFs(file));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void setSTD(File in, File out, File err) {
|
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) {
|
for (var mod : mods) {
|
||||||
mod.fs.protocols.put("std", std);
|
mod.fs.protocols.put("std", new STDFilesystem(in, out, err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void start() {
|
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.codeFolder = folder;
|
||||||
this.dataFolder = dataFolder;
|
this.dataFolder = dataFolder;
|
||||||
|
this.debugServer = server;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,9 +9,13 @@ public interface ChatMessageCallback {
|
|||||||
public boolean cancelled;
|
public boolean cancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(ChatArgs args);
|
boolean execute(ChatArgs args);
|
||||||
|
|
||||||
public static final Event<ChatMessageCallback> EVENT = EventFactory.createArrayBacked(ChatMessageCallback.class, arr -> 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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
19
src/java/me/topchetoeu/mcscript/events/PlayerBlockPlaceEvent.java
Executable file
19
src/java/me/topchetoeu/mcscript/events/PlayerBlockPlaceEvent.java
Executable 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;
|
||||||
|
});
|
||||||
|
}
|
31
src/java/me/topchetoeu/mcscript/events/ScreenHandlerEvents.java
Executable file
31
src/java/me/topchetoeu/mcscript/events/ScreenHandlerEvents.java
Executable 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;
|
||||||
|
});
|
||||||
|
}
|
66
src/java/me/topchetoeu/mcscript/lib/MCInternals.java
Normal file
66
src/java/me/topchetoeu/mcscript/lib/MCInternals.java
Normal 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));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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)));
|
||||||
|
}
|
||||||
|
}
|
326
src/java/me/topchetoeu/mcscript/lib/server/ServerLib.java
Normal file
326
src/java/me/topchetoeu/mcscript/lib/server/ServerLib.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
80
src/java/me/topchetoeu/mcscript/lib/utils/EventLib.java
Normal file
80
src/java/me/topchetoeu/mcscript/lib/utils/EventLib.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
163
src/java/me/topchetoeu/mcscript/lib/utils/LocationLib.java
Normal file
163
src/java/me/topchetoeu/mcscript/lib/utils/LocationLib.java
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
24
src/java/me/topchetoeu/mcscript/mixin/BlockItemMixin.java
Executable file
24
src/java/me/topchetoeu/mcscript/mixin/BlockItemMixin.java
Executable 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);
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@ import net.minecraft.client.MinecraftClient;
|
|||||||
|
|
||||||
@Mixin(Keyboard.class)
|
@Mixin(Keyboard.class)
|
||||||
public class KeyboardMixin {
|
public class KeyboardMixin {
|
||||||
|
|
||||||
@Inject(method = "onChar", at = @At("HEAD"))
|
@Inject(method = "onChar", at = @At("HEAD"))
|
||||||
private void onChar(long window, int codePoint, int modifiers, CallbackInfo cbi) {
|
private void onChar(long window, int codePoint, int modifiers, CallbackInfo cbi) {
|
||||||
var client = MinecraftClient.getInstance();
|
var client = MinecraftClient.getInstance();
|
||||||
|
34
src/java/me/topchetoeu/mcscript/mixin/ScreenHandlerMixin.java
Executable file
34
src/java/me/topchetoeu/mcscript/mixin/ScreenHandlerMixin.java
Executable 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();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user