21 TPS
This commit is contained in:
13
src/api/main/java/co/aikar/timings/ThreadAssertion.java
Normal file
13
src/api/main/java/co/aikar/timings/ThreadAssertion.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package co.aikar.timings;
|
||||
|
||||
public class ThreadAssertion {
|
||||
private static boolean mainThread;
|
||||
|
||||
public static boolean isMainThread() {
|
||||
return mainThread;
|
||||
}
|
||||
|
||||
static boolean setMainThread(boolean is) {
|
||||
return mainThread = is;
|
||||
}
|
||||
}
|
||||
78
src/api/main/java/co/aikar/timings/Timing.java
Normal file
78
src/api/main/java/co/aikar/timings/Timing.java
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* This file is licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package co.aikar.timings;
|
||||
|
||||
/**
|
||||
* Provides an ability to time sections of code within the Minecraft Server
|
||||
*/
|
||||
public interface Timing extends AutoCloseable {
|
||||
/**
|
||||
* Starts timing the execution until {@link #stopTiming()} is called.
|
||||
*
|
||||
* @return Timing
|
||||
*/
|
||||
Timing startTiming();
|
||||
default Timing startTiming(boolean assertThread) { return startTiming(); }; // Akarin
|
||||
|
||||
/**
|
||||
* <p>Stops timing and records the data. Propagates the data up to group handlers.</p>
|
||||
*
|
||||
* Will automatically be called when this Timing is used with try-with-resources
|
||||
*/
|
||||
void stopTiming();
|
||||
|
||||
/**
|
||||
* Starts timing the execution until {@link #stopTiming()} is called.
|
||||
*
|
||||
* But only if we are on the primary thread.
|
||||
*
|
||||
* @return Timing
|
||||
*/
|
||||
Timing startTimingIfSync();
|
||||
default Timing startTimingIfSync(boolean assertThread) { return startTimingIfSync(); }; // Akarin
|
||||
|
||||
/**
|
||||
* <p>Stops timing and records the data. Propagates the data up to group handlers.</p>
|
||||
*
|
||||
* <p>Will automatically be called when this Timing is used with try-with-resources</p>
|
||||
*
|
||||
* But only if we are on the primary thread.
|
||||
*/
|
||||
void stopTimingIfSync();
|
||||
|
||||
/**
|
||||
* Stops timing and disregards current timing data.
|
||||
*/
|
||||
void abort();
|
||||
|
||||
/**
|
||||
* Used internally to get the actual backing Handler in the case of delegated Handlers
|
||||
*
|
||||
* @return TimingHandler
|
||||
*/
|
||||
TimingHandler getTimingHandler();
|
||||
|
||||
@Override
|
||||
void close();
|
||||
}
|
||||
222
src/api/main/java/co/aikar/timings/TimingHandler.java
Normal file
222
src/api/main/java/co/aikar/timings/TimingHandler.java
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* This file is licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package co.aikar.timings;
|
||||
|
||||
import co.aikar.util.LoadingIntMap;
|
||||
import io.akarin.server.core.AkarinGlobalConfig;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
|
||||
class TimingHandler implements Timing {
|
||||
|
||||
private static AtomicInteger idPool = new AtomicInteger(1);
|
||||
final int id = idPool.getAndIncrement();
|
||||
|
||||
final String name;
|
||||
private final boolean verbose;
|
||||
|
||||
private final Int2ObjectOpenHashMap<TimingData> children = new LoadingIntMap<>(TimingData::new);
|
||||
|
||||
final TimingData record;
|
||||
private final TimingHandler groupHandler;
|
||||
|
||||
private long start = 0;
|
||||
private int timingDepth = 0;
|
||||
private boolean added;
|
||||
private boolean timed;
|
||||
private boolean enabled;
|
||||
private TimingHandler parent;
|
||||
|
||||
TimingHandler(TimingIdentifier id) {
|
||||
if (id.name.startsWith("##")) {
|
||||
verbose = true;
|
||||
this.name = id.name.substring(3);
|
||||
} else {
|
||||
this.name = id.name;
|
||||
verbose = false;
|
||||
}
|
||||
|
||||
this.record = new TimingData(this.id);
|
||||
this.groupHandler = id.groupHandler;
|
||||
|
||||
TimingIdentifier.getGroup(id.group).handlers.add(this);
|
||||
checkEnabled();
|
||||
}
|
||||
|
||||
final void checkEnabled() {
|
||||
enabled = Timings.timingsEnabled && (!verbose || Timings.verboseEnabled);
|
||||
}
|
||||
|
||||
void processTick(boolean violated) {
|
||||
if (timingDepth != 0 || record.getCurTickCount() == 0) {
|
||||
timingDepth = 0;
|
||||
start = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
record.processTick(violated);
|
||||
for (TimingData handler : children.values()) {
|
||||
handler.processTick(violated);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
// Akarin start
|
||||
public Timing startTimingIfSync() {
|
||||
return startTiming(false);
|
||||
}
|
||||
@Override
|
||||
// Akarin end
|
||||
public Timing startTimingIfSync(boolean assertThread) {
|
||||
startTiming(assertThread);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopTimingIfSync() {
|
||||
stopTiming();
|
||||
}
|
||||
|
||||
// Akarin start
|
||||
@Override
|
||||
public Timing startTiming() {
|
||||
return startTiming(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timing startTiming(boolean assertThread) {
|
||||
if (enabled && (ThreadAssertion.isMainThread() || Bukkit.isPrimaryThread()) /*&& ++timingDepth == 1*/) {
|
||||
if (AkarinGlobalConfig.lazyThreadAssertion && assertThread) ThreadAssertion.setMainThread(true);
|
||||
if (++timingDepth != 1) return this;
|
||||
// Akarin end
|
||||
start = System.nanoTime();
|
||||
parent = TimingsManager.CURRENT;
|
||||
TimingsManager.CURRENT = this;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopTiming() {
|
||||
// Akarin start
|
||||
if (enabled && timingDepth > 0 && (ThreadAssertion.isMainThread() || Bukkit.isPrimaryThread()) /*&& --timingDepth == 0 && start != 0*/) {
|
||||
if (AkarinGlobalConfig.lazyThreadAssertion) ThreadAssertion.setMainThread(false);
|
||||
if (--timingDepth != 0 || start == 0) return;
|
||||
// Akarin end
|
||||
addDiff(System.nanoTime() - start);
|
||||
start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort() {
|
||||
if (enabled && timingDepth > 0) {
|
||||
start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void addDiff(long diff) {
|
||||
if (TimingsManager.CURRENT == this) {
|
||||
TimingsManager.CURRENT = parent;
|
||||
if (parent != null) {
|
||||
parent.children.get(id).add(diff);
|
||||
}
|
||||
}
|
||||
record.add(diff);
|
||||
if (!added) {
|
||||
added = true;
|
||||
timed = true;
|
||||
TimingsManager.HANDLERS.add(this);
|
||||
}
|
||||
if (groupHandler != null) {
|
||||
groupHandler.addDiff(diff);
|
||||
groupHandler.children.get(id).add(diff);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset this timer, setting all values to zero.
|
||||
*
|
||||
* @param full
|
||||
*/
|
||||
void reset(boolean full) {
|
||||
record.reset();
|
||||
if (full) {
|
||||
timed = false;
|
||||
}
|
||||
start = 0;
|
||||
timingDepth = 0;
|
||||
added = false;
|
||||
children.clear();
|
||||
checkEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimingHandler getTimingHandler() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return (this == o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is simply for the Closeable interface so it can be used with
|
||||
* try-with-resources ()
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
stopTimingIfSync();
|
||||
}
|
||||
|
||||
public boolean isSpecial() {
|
||||
return this == TimingsManager.FULL_SERVER_TICK || this == TimingsManager.TIMINGS_TICK;
|
||||
}
|
||||
|
||||
boolean isTimed() {
|
||||
return timed;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
TimingData[] cloneChildren() {
|
||||
final TimingData[] clonedChildren = new TimingData[children.size()];
|
||||
int i = 0;
|
||||
for (TimingData child : children.values()) {
|
||||
clonedChildren[i++] = child.clone();
|
||||
}
|
||||
return clonedChildren;
|
||||
}
|
||||
}
|
||||
@@ -10,15 +10,12 @@ import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
@SuppressWarnings({"UnusedIsStillUsed", "unused"})
|
||||
public class AkarinGlobalConfig {
|
||||
public final static Logger LOGGER = LogManager.getLogger("Akarin");
|
||||
|
||||
private static File CONFIG_FILE;
|
||||
private static final String HEADER = "This is the global configuration file for Akarin.\n"
|
||||
+ "Some options may impact gameplay, so use with caution,\n"
|
||||
@@ -36,7 +33,8 @@ public class AkarinGlobalConfig {
|
||||
config.load(CONFIG_FILE);
|
||||
} catch (IOException ex) {
|
||||
} catch (InvalidConfigurationException ex) {
|
||||
LOGGER.error("Could not load akarin.yml, please correct your syntax errors", ex);
|
||||
Bukkit.getLogger().severe("Could not load akarin.yml, please correct your syntax errors");
|
||||
ex.printStackTrace();
|
||||
throw Throwables.propagate(ex);
|
||||
}
|
||||
config.options().header(HEADER);
|
||||
@@ -57,7 +55,8 @@ public class AkarinGlobalConfig {
|
||||
} catch (InvocationTargetException ex) {
|
||||
throw Throwables.propagate(ex.getCause());
|
||||
} catch (Exception ex) {
|
||||
LOGGER.error("Error invoking " + method, ex);
|
||||
Bukkit.getLogger().severe("Error invoking " + method);
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,7 +65,8 @@ public class AkarinGlobalConfig {
|
||||
try {
|
||||
config.save(CONFIG_FILE);
|
||||
} catch (IOException ex) {
|
||||
LOGGER.error("Could not save " + CONFIG_FILE, ex);
|
||||
Bukkit.getLogger().severe("Could not save " + CONFIG_FILE);
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,4 +165,9 @@ public class AkarinGlobalConfig {
|
||||
private static void fixPhysicsEventBehavior() {
|
||||
fixPhysicsEventBehaviour = getBoolean("alternative.fix-physics-event-behaviour", fixPhysicsEventBehaviour);
|
||||
}
|
||||
|
||||
public static boolean lazyThreadAssertion = true;
|
||||
private static void lazyThreadAssertion() {
|
||||
lazyThreadAssertion = getBoolean("core.lazy-thread-assertion", lazyThreadAssertion);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.akarin.server.core;
|
||||
|
||||
public enum PacketType { // unused yet
|
||||
public enum PacketType {
|
||||
STATUS_OUT_SERVER_INFO,
|
||||
STATUS_OUT_PONG,
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ public class ChunkMap extends Long2ObjectOpenHashMap<Chunk> {
|
||||
}
|
||||
|
||||
public Chunk put(long i, Chunk chunk) {
|
||||
org.spigotmc.AsyncCatcher.catchOp("Async Chunk put"); // Paper
|
||||
chunk.world.timings.syncChunkLoadPostTimer.startTiming(); // Paper
|
||||
//org.spigotmc.AsyncCatcher.catchOp("Async Chunk put"); // Paper // Akarin - comment
|
||||
lastChunkByPos = chunk; // Paper
|
||||
// Paper start
|
||||
Chunk chunk1;
|
||||
@@ -81,7 +81,7 @@ public class ChunkMap extends Long2ObjectOpenHashMap<Chunk> {
|
||||
|
||||
public Chunk remove(long i) {
|
||||
// Paper start
|
||||
org.spigotmc.AsyncCatcher.catchOp("Async Chunk remove");
|
||||
//org.spigotmc.AsyncCatcher.catchOp("Async Chunk remove"); // Akarin - comment
|
||||
Chunk chunk;
|
||||
synchronized (this) {
|
||||
// synchronize so any async gets are safe
|
||||
|
||||
@@ -5,6 +5,9 @@ import com.destroystokyo.paper.profile.CraftPlayerProfile;
|
||||
import com.destroystokyo.paper.profile.PlayerProfile;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
||||
import co.aikar.timings.ThreadAssertion;
|
||||
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.BlockFace;
|
||||
@@ -115,7 +118,7 @@ public final class MCUtil {
|
||||
* @return
|
||||
*/
|
||||
public static void ensureMain(String reason, Runnable run) {
|
||||
if (AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().primaryThread) {
|
||||
if (AsyncCatcher.enabled && !ThreadAssertion.isMainThread() && Thread.currentThread() != MinecraftServer.getServer().primaryThread) { // Akarin
|
||||
if (reason != null) {
|
||||
new IllegalStateException("Asynchronous " + reason + "!").printStackTrace();
|
||||
}
|
||||
@@ -140,7 +143,7 @@ public final class MCUtil {
|
||||
* @return
|
||||
*/
|
||||
public static <T> T ensureMain(String reason, Supplier<T> run) {
|
||||
if (AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().primaryThread) {
|
||||
if (AsyncCatcher.enabled && !ThreadAssertion.isMainThread() && Thread.currentThread() != MinecraftServer.getServer().primaryThread) { // Akarin
|
||||
if (reason != null) {
|
||||
new IllegalStateException("Asynchronous " + reason + "! Blocking thread until it returns ").printStackTrace();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.mojang.authlib.GameProfileRepository;
|
||||
import com.mojang.authlib.minecraft.MinecraftSessionService;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufOutputStream;
|
||||
import io.netty.buffer.Unpooled;
|
||||
@@ -65,6 +66,7 @@ import org.bukkit.event.server.ServerLoadEvent;
|
||||
// CraftBukkit end
|
||||
import org.spigotmc.SlackActivityAccountant; // Spigot
|
||||
import co.aikar.timings.MinecraftTimings; // Paper
|
||||
import co.aikar.timings.ThreadAssertion;
|
||||
|
||||
public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStatistics, ICommandListener, Runnable {
|
||||
|
||||
@@ -1704,7 +1706,7 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati
|
||||
}
|
||||
|
||||
public boolean isMainThread() {
|
||||
return Thread.currentThread() == this.serverThread;
|
||||
return ThreadAssertion.isMainThread() || Thread.currentThread() == this.serverThread; // Akarin
|
||||
}
|
||||
|
||||
public int aw() {
|
||||
|
||||
@@ -69,6 +69,7 @@ import com.destroystokyo.paper.event.player.IllegalPacketEvent; // Paper
|
||||
import com.destroystokyo.paper.event.player.PlayerJumpEvent; // Paper
|
||||
import co.aikar.timings.MinecraftTimings; // Paper
|
||||
// CraftBukkit end
|
||||
import co.aikar.timings.ThreadAssertion;
|
||||
|
||||
public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
||||
|
||||
@@ -1756,7 +1757,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
||||
|
||||
if (!async && s.startsWith("/")) {
|
||||
// Paper Start
|
||||
if (!org.spigotmc.AsyncCatcher.shuttingDown && !org.bukkit.Bukkit.isPrimaryThread()) {
|
||||
if (!org.spigotmc.AsyncCatcher.shuttingDown && !ThreadAssertion.isMainThread() && !org.bukkit.Bukkit.isPrimaryThread()) {
|
||||
final String fCommandLine = s;
|
||||
MinecraftServer.LOGGER.log(org.apache.logging.log4j.Level.ERROR, "Command Dispatched Async: " + fCommandLine);
|
||||
MinecraftServer.LOGGER.log(org.apache.logging.log4j.Level.ERROR, "Please notify author of plugin causing this execution to fix this bug! see: http://bit.ly/1oSiM6C", new Throwable());
|
||||
|
||||
@@ -139,6 +139,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
|
||||
import co.aikar.timings.ThreadAssertion;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufOutputStream;
|
||||
import io.netty.buffer.Unpooled;
|
||||
@@ -709,7 +710,7 @@ public final class CraftServer implements Server {
|
||||
org.spigotmc.AsyncCatcher.catchOp( "command dispatch" ); // Spigot
|
||||
|
||||
// Paper Start
|
||||
if (!org.spigotmc.AsyncCatcher.shuttingDown && !Bukkit.isPrimaryThread()) {
|
||||
if (!org.spigotmc.AsyncCatcher.shuttingDown && !ThreadAssertion.isMainThread() && !Bukkit.isPrimaryThread()) { // Akarin
|
||||
final CommandSender fSender = sender;
|
||||
final String fCommandLine = commandLine;
|
||||
Bukkit.getLogger().log(Level.SEVERE, "Command Dispatched Async: " + commandLine);
|
||||
@@ -1723,7 +1724,7 @@ public final class CraftServer implements Server {
|
||||
|
||||
@Override
|
||||
public boolean isPrimaryThread() {
|
||||
return Thread.currentThread().equals(console.primaryThread);
|
||||
return ThreadAssertion.isMainThread() || Thread.currentThread().equals(console.primaryThread);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -40,7 +40,7 @@ class ChunkIOProvider implements AsynchronousExecutor.CallBackProvider<QueuedChu
|
||||
queuedChunk.provider.getChunkAt(queuedChunk.x, queuedChunk.z, true, true); // Paper - actually call original if it was already loaded
|
||||
return;
|
||||
}
|
||||
try (Timing ignored = queuedChunk.provider.world.timings.chunkIOStage2.startTimingIfSync()) { // Paper
|
||||
try (Timing ignored = queuedChunk.provider.world.timings.chunkIOStage2.startTimingIfSync(true)) { // Paper // Akarin
|
||||
|
||||
queuedChunk.loader.loadEntities(queuedChunk.compound.getCompound("Level"), chunk);
|
||||
chunk.setLastSaved(queuedChunk.provider.world.getTime());
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.spigotmc;
|
||||
|
||||
import co.aikar.timings.ThreadAssertion;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
public class AsyncCatcher
|
||||
@@ -10,7 +11,7 @@ public class AsyncCatcher
|
||||
|
||||
public static void catchOp(String reason)
|
||||
{
|
||||
if ( enabled && Thread.currentThread() != MinecraftServer.getServer().primaryThread )
|
||||
if ( enabled && !ThreadAssertion.isMainThread() && Thread.currentThread() != MinecraftServer.getServer().primaryThread )
|
||||
{
|
||||
throw new IllegalStateException( "Asynchronous " + reason + "!" );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user