Port async catcher
This commit is contained in:
parent
9c359490aa
commit
fcc143a24f
|
@ -7,7 +7,7 @@ A Bukkit server implementation utilizing Mixin.
|
|||
| Minecraft | Forge | Status | Build |
|
||||
| :----: | :----: | :---: | :---: |
|
||||
| 1.16.x | 34.1.7 | ACTIVE | [![1.16 Status](https://img.shields.io/appveyor/build/IzzelAliz/arclight-16?style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-16) |
|
||||
| 1.15.x | 31.2.37 | ACTIVE | [![1.15 Status](https://img.shields.io/appveyor/build/IzzelAliz/arclight-15?style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-15) |
|
||||
| 1.15.x | 31.2.45 | ACTIVE | [![1.15 Status](https://img.shields.io/appveyor/build/IzzelAliz/arclight-15?style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-15) |
|
||||
| 1.14.x | 28.2.0 | [LEGACY](https://github.com/IzzelAliz/Arclight/releases/tag/1.0.6) | [![1.14 Status](https://img.shields.io/appveyor/build/IzzelAliz/arclight?style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight) |
|
||||
|
||||
* Legacy version still accepts pull requests.
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.izzel.arclight.api;
|
|||
|
||||
import sun.reflect.CallerSensitive;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Field;
|
||||
import java.security.ProtectionDomain;
|
||||
|
@ -11,6 +12,7 @@ public class Unsafe {
|
|||
|
||||
private static final sun.misc.Unsafe unsafe;
|
||||
private static final MethodHandles.Lookup lookup;
|
||||
private static final MethodHandle defineClass;
|
||||
|
||||
static {
|
||||
try {
|
||||
|
@ -22,11 +24,24 @@ public class Unsafe {
|
|||
Object base = unsafe.staticFieldBase(field);
|
||||
long offset = unsafe.staticFieldOffset(field);
|
||||
lookup = (MethodHandles.Lookup) unsafe.getObject(base, offset);
|
||||
defineClass = lookup.unreflect(ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ProtectionDomain.class));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T getStatic(Class<?> cl, String name) {
|
||||
try {
|
||||
Unsafe.ensureClassInitialized(cl);
|
||||
Field field = cl.getDeclaredField(name);
|
||||
Object materialByNameBase = Unsafe.staticFieldBase(field);
|
||||
long materialByNameOffset = Unsafe.staticFieldOffset(field);
|
||||
return (T) Unsafe.getObject(materialByNameBase, materialByNameOffset);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static MethodHandles.Lookup lookup() {
|
||||
return lookup;
|
||||
}
|
||||
|
@ -337,7 +352,12 @@ public class Unsafe {
|
|||
}
|
||||
|
||||
public static Class<?> defineClass(String s, byte[] bytes, int i, int i1, ClassLoader classLoader, ProtectionDomain protectionDomain) {
|
||||
return unsafe.defineClass(s, bytes, i, i1, classLoader, protectionDomain);
|
||||
try {
|
||||
return (Class<?>) defineClass.bindTo(classLoader).invoke(s, bytes, i , i1, protectionDomain);
|
||||
} catch (Throwable throwable) {
|
||||
throwException(throwable);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Class<?> defineAnonymousClass(Class<?> aClass, byte[] bytes, Object[] objects) {
|
||||
|
|
|
@ -3,10 +3,15 @@ package io.izzel.arclight.common.asm;
|
|||
import cpw.mods.modlauncher.serviceapi.ILaunchPluginService;
|
||||
import io.izzel.arclight.common.mod.util.log.ArclightI18nLogger;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
import org.objectweb.asm.tree.VarInsnNode;
|
||||
import org.spongepowered.asm.launch.MixinLaunchPlugin;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
|
@ -36,6 +41,7 @@ public class ArclightImplementer implements ILaunchPluginService {
|
|||
this.transformerLoader = transformerLoader;
|
||||
this.implementers.put("inventory", new InventoryImplementer());
|
||||
this.implementers.put("switch", SwitchTableFixer.INSTANCE);
|
||||
this.implementers.put("async", AsyncCatcher.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -79,4 +85,15 @@ public class ArclightImplementer implements ILaunchPluginService {
|
|||
public boolean processClass(Phase phase, ClassNode classNode, Type classType) {
|
||||
throw new IllegalStateException("Outdated ModLauncher");
|
||||
}
|
||||
|
||||
public static void loadArgs(InsnList list, MethodNode methodNode, Type[] types, int i) {
|
||||
if (!Modifier.isStatic(methodNode.access)) {
|
||||
list.add(new VarInsnNode(Opcodes.ALOAD, i));
|
||||
i += 1;
|
||||
}
|
||||
for (Type type : types) {
|
||||
list.add(new VarInsnNode(type.getOpcode(Opcodes.ILOAD), i));
|
||||
i += type.getSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,255 @@
|
|||
package io.izzel.arclight.common.asm;
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import cpw.mods.modlauncher.serviceapi.ILaunchPluginService;
|
||||
import io.izzel.arclight.api.Unsafe;
|
||||
import io.izzel.arclight.common.mod.server.ArclightServer;
|
||||
import io.izzel.arclight.i18n.ArclightConfig;
|
||||
import io.izzel.arclight.i18n.conf.AsyncCatcherSpec;
|
||||
import org.apache.logging.log4j.Marker;
|
||||
import org.apache.logging.log4j.MarkerManager;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.GeneratorAdapter;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.FieldInsnNode;
|
||||
import org.objectweb.asm.tree.FieldNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.InsnNode;
|
||||
import org.objectweb.asm.tree.JumpInsnNode;
|
||||
import org.objectweb.asm.tree.LabelNode;
|
||||
import org.objectweb.asm.tree.LdcInsnNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
import org.objectweb.asm.tree.TypeInsnNode;
|
||||
import org.objectweb.asm.tree.VarInsnNode;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.util.Constants;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class AsyncCatcher implements Implementer {
|
||||
|
||||
public static final AsyncCatcher INSTANCE = new AsyncCatcher();
|
||||
private static final Marker MARKER = MarkerManager.getMarker("ASYNC_CATCHER");
|
||||
private static final CallbackInfoReturnable<?> NOOP = new CallbackInfoReturnable<>("noop", false);
|
||||
private static final AtomicInteger COUNTER = new AtomicInteger(0);
|
||||
|
||||
private final boolean dump;
|
||||
private final boolean warn;
|
||||
private final AsyncCatcherSpec.Operation defaultOp;
|
||||
private final Map<String, Map<String, String>> reasons;
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
public AsyncCatcher() {
|
||||
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
|
||||
this.reasons = gson.fromJson(
|
||||
new InputStreamReader(AsyncCatcher.class.getResourceAsStream("/async_catcher.json")),
|
||||
new TypeToken<Map<String, Map<String, String>>>() {}.getType()
|
||||
);
|
||||
this.defaultOp = ArclightConfig.spec().getAsyncCatcher().getDefaultOp();
|
||||
this.dump = ArclightConfig.spec().getAsyncCatcher().isDump();
|
||||
this.warn = ArclightConfig.spec().getAsyncCatcher().isWarn();
|
||||
this.classLoader = Thread.currentThread().getContextClassLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processClass(ClassNode node, ILaunchPluginService.ITransformerLoader transformerLoader) {
|
||||
Map<String, String> map = reasons.get(node.name);
|
||||
if (map != null) {
|
||||
boolean found = false;
|
||||
List<MethodNode> methods = node.methods;
|
||||
for (int i = 0, methodsSize = methods.size(); i < methodsSize; i++) {
|
||||
MethodNode method = methods.get(i);
|
||||
String reason = map.get(method.name + method.desc);
|
||||
if (reason != null) {
|
||||
found = true;
|
||||
injectCheck(node, method, reason);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void injectCheck(ClassNode node, MethodNode methodNode, String reason) {
|
||||
ArclightImplementer.LOGGER.debug(MARKER, "Injecting {}/{}{} for reason {}", node.name, methodNode.name, methodNode.desc, reason);
|
||||
AsyncCatcherSpec.Operation operation = ArclightConfig.spec().getAsyncCatcher().getOverrides().getOrDefault(reason, defaultOp);
|
||||
InsnList insnList = new InsnList();
|
||||
LabelNode labelNode = new LabelNode(new Label());
|
||||
LabelNode labelNode1 = new LabelNode(new Label());
|
||||
insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "io/izzel/arclight/common/mod/server/ArclightServer", "isPrimaryThread", "()Z"));
|
||||
insnList.add(new JumpInsnNode(Opcodes.IFNE, labelNode));
|
||||
instantiateCallback(node, methodNode, insnList);
|
||||
insnList.add(new FieldInsnNode(Opcodes.GETSTATIC, Type.getType(AsyncCatcherSpec.Operation.class).getInternalName(), operation.name(), Type.getType(AsyncCatcherSpec.Operation.class).getDescriptor()));
|
||||
insnList.add(new LdcInsnNode(reason));
|
||||
insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, Type.getType(AsyncCatcher.class).getInternalName(), "checkOp", "(Ljava/util/function/Supplier;Lio/izzel/arclight/i18n/conf/AsyncCatcherSpec$Operation;Ljava/lang/String;)Lorg/spongepowered/asm/mixin/injection/callback/CallbackInfoReturnable;"));
|
||||
Type returnType = Type.getMethodType(methodNode.desc).getReturnType();
|
||||
boolean hasReturn = !returnType.equals(Type.VOID_TYPE);
|
||||
if (hasReturn) {
|
||||
insnList.add(new InsnNode(Opcodes.DUP));
|
||||
}
|
||||
insnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getType(CallbackInfoReturnable.class).getInternalName(), "isCancelled", "()Z"));
|
||||
insnList.add(new JumpInsnNode(Opcodes.IFEQ, hasReturn ? labelNode1 : labelNode));
|
||||
if (hasReturn) {
|
||||
insnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getType(CallbackInfoReturnable.class).getInternalName(), getReturnAccessor(returnType), getReturnDescriptor(returnType)));
|
||||
if (returnType.getSort() > Type.DOUBLE) {
|
||||
insnList.add(new TypeInsnNode(Opcodes.CHECKCAST, returnType.getInternalName()));
|
||||
}
|
||||
insnList.add(new InsnNode(returnType.getOpcode(Opcodes.IRETURN)));
|
||||
} else {
|
||||
insnList.add(new InsnNode(Opcodes.RETURN));
|
||||
}
|
||||
insnList.add(labelNode1);
|
||||
insnList.add(new InsnNode(Opcodes.POP));
|
||||
insnList.add(labelNode);
|
||||
methodNode.instructions.insert(insnList);
|
||||
}
|
||||
|
||||
private void instantiateCallback(ClassNode node, MethodNode methodNode, InsnList insnList) {
|
||||
MethodNode bridge;
|
||||
if (Modifier.isPrivate(methodNode.access)) {
|
||||
bridge = createBridge(node, methodNode);
|
||||
} else {
|
||||
bridge = methodNode;
|
||||
}
|
||||
|
||||
ClassNode classNode = new ClassNode();
|
||||
String desc = createImplType(node, methodNode, classNode, bridge);
|
||||
|
||||
insnList.add(new TypeInsnNode(Opcodes.NEW, classNode.name));
|
||||
insnList.add(new InsnNode(Opcodes.DUP));
|
||||
Type methodType = Type.getMethodType(methodNode.desc);
|
||||
ArclightImplementer.loadArgs(insnList, methodNode, methodType.getArgumentTypes(), 0);
|
||||
insnList.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, classNode.name, "<init>", desc));
|
||||
}
|
||||
|
||||
private String createImplType(ClassNode node, MethodNode methodNode, ClassNode classNode, MethodNode bridge) {
|
||||
classNode.version = Opcodes.V1_8;
|
||||
classNode.name = node.name + "$AsyncCatcher$" + COUNTER.getAndIncrement();
|
||||
classNode.access = Opcodes.ACC_SYNTHETIC | Opcodes.ACC_SUPER | Opcodes.ACC_FINAL;
|
||||
classNode.superName = "java/lang/Object";
|
||||
classNode.interfaces.add(Type.getType(Supplier.class).getInternalName());
|
||||
List<Type> types = new ArrayList<>();
|
||||
if (!Modifier.isStatic(methodNode.access)) {
|
||||
types.add(Type.getObjectType(node.name));
|
||||
}
|
||||
types.addAll(Arrays.asList(Type.getArgumentTypes(methodNode.desc)));
|
||||
|
||||
for (int i = 0; i < types.size(); i++) {
|
||||
FieldNode fieldNode = new FieldNode(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, "x" + i, types.get(i).getDescriptor(), null, null);
|
||||
classNode.fields.add(fieldNode);
|
||||
}
|
||||
|
||||
MethodNode init = new MethodNode();
|
||||
init.name = "<init>";
|
||||
init.desc = Type.getMethodType(Type.VOID_TYPE, types.toArray(new Type[0])).getDescriptor();
|
||||
init.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||
init.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V"));
|
||||
|
||||
int offset = 1;
|
||||
for (int i = 0; i < types.size(); i++) {
|
||||
init.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||
init.instructions.add(new VarInsnNode(types.get(i).getOpcode(Opcodes.ILOAD), offset));
|
||||
init.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, "x" + i, types.get(i).getDescriptor()));
|
||||
offset += types.get(i).getSize();
|
||||
}
|
||||
init.instructions.add(new InsnNode(Opcodes.RETURN));
|
||||
classNode.methods.add(init);
|
||||
|
||||
MethodNode get = new MethodNode();
|
||||
get.name = "get";
|
||||
get.desc = "()Ljava/lang/Object;";
|
||||
GeneratorAdapter adapter = new GeneratorAdapter(get, Opcodes.ACC_PUBLIC, get.name, get.desc);
|
||||
for (int i = 0; i < types.size(); i++) {
|
||||
adapter.loadThis();
|
||||
adapter.getField(Type.getObjectType(classNode.name), "x" + i, types.get(i));
|
||||
}
|
||||
get.instructions.add(new MethodInsnNode(
|
||||
Modifier.isStatic(methodNode.access) ? Opcodes.INVOKESTATIC : Opcodes.INVOKEVIRTUAL,
|
||||
node.name, bridge.name, bridge.desc
|
||||
));
|
||||
adapter.valueOf(Type.getReturnType(bridge.desc));
|
||||
adapter.returnValue();
|
||||
classNode.methods.add(get);
|
||||
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
classNode.accept(writer);
|
||||
byte[] bytes = writer.toByteArray();
|
||||
Unsafe.defineClass(Type.getObjectType(classNode.name).getClassName(), bytes, 0, bytes.length, this.classLoader, AsyncCatcher.class.getProtectionDomain());
|
||||
ArclightImplementer.LOGGER.debug(MARKER, "Defined impl callback class {}", classNode.name);
|
||||
return init.desc;
|
||||
}
|
||||
|
||||
private MethodNode createBridge(ClassNode node, MethodNode methodNode) {
|
||||
MethodNode ret = new MethodNode();
|
||||
ret.name = methodNode.name + "$asyncCatcher$" + COUNTER.getAndIncrement();
|
||||
ret.desc = methodNode.desc;
|
||||
ret.access = Opcodes.ACC_PUBLIC | Opcodes.ACC_BRIDGE | Opcodes.ACC_SYNTHETIC;
|
||||
if (Modifier.isStatic(methodNode.access)) {
|
||||
ret.access = ret.access | Opcodes.ACC_STATIC;
|
||||
}
|
||||
Type methodType = Type.getMethodType(methodNode.desc);
|
||||
ArclightImplementer.loadArgs(ret.instructions, methodNode, methodType.getArgumentTypes(), 0);
|
||||
int invokeCode = Modifier.isStatic(methodNode.access) ? Opcodes.INVOKESTATIC : Opcodes.INVOKESPECIAL;
|
||||
ret.instructions.add(new MethodInsnNode(invokeCode, node.name, methodNode.name, methodNode.desc));
|
||||
ret.instructions.add(new InsnNode(methodType.getReturnType().getOpcode(Opcodes.IRETURN)));
|
||||
node.methods.add(ret);
|
||||
ArclightImplementer.LOGGER.debug(MARKER, "Bridge method {}/{}{} created", node.name, ret.name, ret.desc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static String getReturnAccessor(org.objectweb.asm.Type returnType) {
|
||||
if (returnType.getSort() == org.objectweb.asm.Type.OBJECT || returnType.getSort() == org.objectweb.asm.Type.ARRAY) {
|
||||
return "getReturnValue";
|
||||
}
|
||||
return String.format("getReturnValue%s", returnType.getDescriptor());
|
||||
}
|
||||
|
||||
static String getReturnDescriptor(org.objectweb.asm.Type returnType) {
|
||||
if (returnType.getSort() == org.objectweb.asm.Type.OBJECT || returnType.getSort() == org.objectweb.asm.Type.ARRAY) {
|
||||
return String.format("()%s", Constants.OBJECT_DESC);
|
||||
}
|
||||
return String.format("()%s", returnType.getDescriptor());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> CallbackInfoReturnable<T> checkOp(Supplier<T> method, AsyncCatcherSpec.Operation operation, String reason) throws Throwable {
|
||||
if (INSTANCE.warn) {
|
||||
ArclightImplementer.LOGGER.warn(MARKER, "Async " + reason);
|
||||
}
|
||||
IllegalStateException exception = new IllegalStateException("Asynchronous " + reason + "!");
|
||||
if (INSTANCE.dump) {
|
||||
ArclightImplementer.LOGGER.debug(MARKER, "Async " + reason, exception);
|
||||
}
|
||||
switch (operation) {
|
||||
case NONE: return (CallbackInfoReturnable<T>) NOOP;
|
||||
case EXCEPTION: throw exception;
|
||||
case BLOCK: {
|
||||
CallbackInfoReturnable<T> cir = new CallbackInfoReturnable<>(reason, true);
|
||||
CompletableFuture<T> future = CompletableFuture.supplyAsync(method, ArclightServer.getMainThreadExecutor());
|
||||
cir.setReturnValue(future.get(5, TimeUnit.SECONDS));
|
||||
return cir;
|
||||
}
|
||||
case DISPATCH: {
|
||||
ArclightServer.executeOnMainThread(method::get);
|
||||
CallbackInfoReturnable<T> cir = new CallbackInfoReturnable<>(reason, true);
|
||||
cir.cancel();
|
||||
return cir;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("how this can happen?");
|
||||
}
|
||||
}
|
|
@ -295,6 +295,9 @@ public abstract class MinecraftServerMixin extends RecursiveEventLoop<TickDelaye
|
|||
|
||||
private void executeModerately() {
|
||||
this.drainTasks();
|
||||
while (!processQueue.isEmpty()) {
|
||||
processQueue.remove().run();
|
||||
}
|
||||
java.util.concurrent.locks.LockSupport.parkNanos("executing tasks", 1000L);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,9 +15,11 @@ import org.bukkit.craftbukkit.v.command.ColouredConsoleSender;
|
|||
|
||||
import java.io.File;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class ArclightServer {
|
||||
|
||||
private static final Executor mainThreadExecutor = ArclightServer::executeOnMainThread;
|
||||
private static CraftServer server;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
|
@ -49,10 +51,26 @@ public class ArclightServer {
|
|||
return Objects.requireNonNull(server);
|
||||
}
|
||||
|
||||
public static boolean isPrimaryThread() {
|
||||
if (server == null) {
|
||||
return Thread.currentThread().equals(getMinecraftServer().getExecutionThread());
|
||||
} else {
|
||||
return server.isPrimaryThread();
|
||||
}
|
||||
}
|
||||
|
||||
public static MinecraftServer getMinecraftServer() {
|
||||
return ServerLifecycleHooks.getCurrentServer();
|
||||
}
|
||||
|
||||
public static void executeOnMainThread(Runnable runnable) {
|
||||
((MinecraftServerBridge) getMinecraftServer()).bridge$queuedProcess(runnable);
|
||||
}
|
||||
|
||||
public static Executor getMainThreadExecutor() {
|
||||
return mainThreadExecutor;
|
||||
}
|
||||
|
||||
public static World.Environment getEnvironment(RegistryKey<DimensionType> key) {
|
||||
return BukkitRegistry.DIM_MAP.get(key);
|
||||
}
|
||||
|
|
22
arclight-common/src/main/resources/async_catcher.json
Normal file
22
arclight-common/src/main/resources/async_catcher.json
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"net/minecraft/world/server/ServerWorld": {
|
||||
"func_72838_d(Lnet/minecraft/entity/Entity;)Z": "entity add",
|
||||
"func_217465_m(Lnet/minecraft/entity/Entity;)V": "entity register",
|
||||
"removeEntityComplete(Lnet/minecraft/entity/Entity;Z)V": "entity unregister"
|
||||
},
|
||||
"net/minecraft/world/server/ChunkManager$EntityTracker": {
|
||||
"func_219399_a(Lnet/minecraft/entity/player/ServerPlayerEntity;)V": "player tracker clear",
|
||||
"func_219400_b(Lnet/minecraft/entity/player/ServerPlayerEntity;)V": "player tracker update"
|
||||
},
|
||||
"net/minecraft/block/Block": {
|
||||
"func_220082_b(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Z)V": "block place",
|
||||
"func_196243_a(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Z)V": "block remove"
|
||||
},
|
||||
"net/minecraft/entity/LivingEntity": {
|
||||
"func_195064_c(Lnet/minecraft/potion/EffectInstance;)Z": "effect add"
|
||||
},
|
||||
"net/minecraft/world/server/ChunkManager": {
|
||||
"func_219210_a(Lnet/minecraft/entity/Entity;)V": "entity track",
|
||||
"func_219231_b(Lnet/minecraft/entity/Entity;)V": "entity untrack"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package io.izzel.arclight.i18n.conf;
|
||||
|
||||
import ninja.leaping.configurate.objectmapping.Setting;
|
||||
import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@ConfigSerializable
|
||||
public class AsyncCatcherSpec {
|
||||
|
||||
@Setting("dump")
|
||||
private boolean dump;
|
||||
|
||||
@Setting("warn")
|
||||
private boolean warn;
|
||||
|
||||
@Setting("defaultOperation")
|
||||
private Operation defaultOp;
|
||||
|
||||
@Setting("overrides")
|
||||
private Map<String, Operation> overrides;
|
||||
|
||||
public boolean isDump() {
|
||||
return dump;
|
||||
}
|
||||
|
||||
public boolean isWarn() {
|
||||
return warn;
|
||||
}
|
||||
|
||||
public Operation getDefaultOp() {
|
||||
return defaultOp;
|
||||
}
|
||||
|
||||
public Map<String, Operation> getOverrides() {
|
||||
return overrides;
|
||||
}
|
||||
|
||||
public enum Operation {
|
||||
NONE, DISPATCH, BLOCK, EXCEPTION
|
||||
}
|
||||
}
|
|
@ -18,6 +18,9 @@ public class ConfigSpec {
|
|||
@Setting("compatibility")
|
||||
private CompatSpec compatSpec;
|
||||
|
||||
@Setting("async-catcher")
|
||||
private AsyncCatcherSpec asyncCatcherSpec;
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
@ -33,4 +36,8 @@ public class ConfigSpec {
|
|||
public CompatSpec getCompat() {
|
||||
return compatSpec;
|
||||
}
|
||||
|
||||
public AsyncCatcherSpec getAsyncCatcher() {
|
||||
return asyncCatcherSpec;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,3 +12,10 @@ compatibility {
|
|||
entity-property-overrides {
|
||||
}
|
||||
}
|
||||
async-catcher {
|
||||
dump = true
|
||||
warn = true
|
||||
defaultOperation = block
|
||||
overrides {
|
||||
}
|
||||
}
|
|
@ -81,4 +81,13 @@ comments {
|
|||
]
|
||||
locale.comment = "语言/国际化相关设置"
|
||||
optimization.comment = "服务端优化相关设置"
|
||||
async-catcher.comment = [
|
||||
"异步捕获相关设置"
|
||||
"Async Catcher 共有四种模式"
|
||||
"NONE - 保持在当前线程执行"
|
||||
"DISPATCH - 不阻塞地发布到主线程"
|
||||
"BLOCK - 发不到主线程并等待结果"
|
||||
"EXCEPTION - 抛出异常"
|
||||
]
|
||||
async-catcher.dump.comment = "是否在 debug 日志中打印堆栈信息"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user