Upstream Paper and misc

This commit is contained in:
Sotr
2018-08-13 04:28:45 +08:00
parent 46e8595949
commit 0c3fec2c10
10 changed files with 165 additions and 1904 deletions

View File

@@ -1,18 +1,29 @@
package io.akarin.api.internal.utils;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public class ReentrantSpinningLock {
private final AtomicBoolean singleLock = new AtomicBoolean(false);
/*
* Impl Note:
* A write lock can reentrant as a read lock, while a
* read lock is not allowed to reentrant as a write lock.
*/
private final AtomicBoolean writeLocked = new AtomicBoolean(false);
// --------- Thread local restricted fields ---------
private long heldThreadId = 0;
private int reentrantLocks = 0;
/**
* Lock as a typical write lock
*/
public void lock() {
long currentThreadId = Thread.currentThread().getId();
if (heldThreadId == currentThreadId) {
reentrantLocks++;
} else {
while (!singleLock.compareAndSet(false, true)) ; // In case acquire one lock concurrently
while (!writeLocked.compareAndSet(false, true)) ; // In case acquire one lock concurrently
heldThreadId = currentThreadId;
}
}
@@ -20,9 +31,68 @@ public class ReentrantSpinningLock {
public void unlock() {
if (reentrantLocks == 0) {
heldThreadId = 0;
singleLock.set(false);
if (readerThreads.getAndDecrement() == 1) { // Micro-optimization: this saves one subtract
writeLocked.set(false);
}
} else {
--reentrantLocks;
}
}
private final AtomicInteger readerThreads = new AtomicInteger(0);
/**
* Lock as a typical read lock
*/
public void lockWeak() {
long currentThreadId = Thread.currentThread().getId();
if (heldThreadId == currentThreadId) {
reentrantLocks++;
} else {
if (readerThreads.get() == 0) {
while (!writeLocked.compareAndSet(false, true)) ; // Block future write lock
}
heldThreadId = currentThreadId;
readerThreads.getAndIncrement(); // Micro-optimization: this saves one plus
}
}
public void unlockWeak() {
if (reentrantLocks == 0) {
heldThreadId = 0;
writeLocked.set(false);
} else {
--reentrantLocks;
}
}
// --------- Wrappers to allow typical usages ---------
private SpinningWriteLock wrappedWriteLock = new SpinningWriteLock();
private SpinningReadLock wrappedReadLock = new SpinningReadLock();
public class SpinningWriteLock {
public void lock() {
lock();
}
public void unlock() {
unlock();
}
}
public class SpinningReadLock {
public void lock() {
lockWeak();
}
public void unlock() {
unlockWeak();
}
}
public SpinningWriteLock writeLock() {
return wrappedWriteLock;
}
public SpinningReadLock readLocked() {
return wrappedReadLock;
}
}

View File

@@ -25,7 +25,7 @@ public class AkarinGlobalConfig {
+ "Some options may impact gameplay, so use with caution,\n"
+ "and make sure you know what each option does before configuring.\n"
+ "\n"
+ "Akarin forums: https://akarin.io/ \n";
+ "Akarin website: https://akarin.io/ \n";
/*========================================================================*/
public static YamlConfiguration config;
static int version;

View File

@@ -6,7 +6,6 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.CraftServer;
import org.spigotmc.RestartCommand;
import org.spigotmc.WatchdogThread;
import org.spongepowered.asm.mixin.Final;
@@ -23,6 +22,10 @@ import net.minecraft.server.MinecraftServer;
public abstract class Watchcat extends Thread {
@Shadow private static WatchdogThread instance;
@Shadow private @Final long timeoutTime;
@Shadow private @Final long earlyWarningEvery; // Paper - Timeout time for just printing a dump but not restarting
@Shadow private @Final long earlyWarningDelay; // Paper
@Shadow public static volatile boolean hasStarted; // Paper
@Shadow private long lastEarlyWarning; // Paper - Keep track of short dump times to avoid spamming console with short dumps
@Shadow private @Final boolean restart;
@Shadow private volatile long lastTick;
@Shadow private volatile boolean stopping;
@@ -39,47 +42,67 @@ public abstract class Watchcat extends Thread {
public void run() {
while (!stopping) {
//
if (lastTick != 0 && System.currentTimeMillis() > lastTick + timeoutTime && !Boolean.getBoolean("disable.watchdog")) { // Paper - Add property to disable
Logger log = Bukkit.getServer().getLogger();
log.log(Level.SEVERE, "Server has stopped responding!");
log.log(Level.SEVERE, "Please report this to https://github.com/Akarin-project/Akarin/issues");
log.log(Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports");
log.log(Level.SEVERE, "Akarin version: " + Bukkit.getServer().getVersion());
//
if (net.minecraft.server.World.haveWeSilencedAPhysicsCrash) {
log.log(Level.SEVERE, "------------------------------");
log.log(Level.SEVERE, "During the run of the server, a physics stackoverflow was supressed");
log.log(Level.SEVERE, "near " + net.minecraft.server.World.blockLocation);
}
// Paper start - Warn in watchdog if an excessive velocity was ever set
if (CraftServer.excessiveVelEx != null) {
log.log(Level.SEVERE, "------------------------------");
log.log(Level.SEVERE, "During the run of the server, a plugin set an excessive velocity on an entity");
log.log(Level.SEVERE, "This may be the cause of the issue, or it may be entirely unrelated");
log.log(Level.SEVERE, CraftServer.excessiveVelEx.getMessage());
for (StackTraceElement stack : CraftServer.excessiveVelEx.getStackTrace()) {
log.log(Level.SEVERE, "\t\t" + stack);
}
}
long currentTime = System.currentTimeMillis(); // Paper - do we REALLY need to call this method multiple times?
if (lastTick != 0 && currentTime > lastTick + earlyWarningEvery && !Boolean.getBoolean("disable.watchdog")) // Paper - Add property to disable and short timeout
{
// Paper start
boolean isLongTimeout = currentTime > lastTick + timeoutTime;
// Don't spam short dumps
if (!isLongTimeout && (earlyWarningEvery <= 0 || !hasStarted || currentTime < lastEarlyWarning + earlyWarningEvery || currentTime < lastTick + earlyWarningDelay))
continue;
lastEarlyWarning = currentTime;
// Paper end
Logger log = Bukkit.getServer().getLogger();
// Paper start - Different message when it's a short timeout
if (isLongTimeout) {
log.log(Level.SEVERE, "The server has stopped responding!");
log.log(Level.SEVERE, "Please report this to https://github.com/Akarin-project/Akarin/issues"); // Akarin
log.log(Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports");
log.log(Level.SEVERE, "Akarin version: " + Bukkit.getServer().getVersion()); // Akarin
//
if (net.minecraft.server.World.haveWeSilencedAPhysicsCrash) {
log.log(Level.SEVERE, "------------------------------");
log.log(Level.SEVERE, "During the run of the server, a physics stackoverflow was supressed");
log.log(Level.SEVERE, "near " + net.minecraft.server.World.blockLocation);
}
// Paper start - Warn in watchdog if an excessive velocity was ever set
if (org.bukkit.craftbukkit.CraftServer.excessiveVelEx != null) {
log.log(Level.SEVERE, "------------------------------");
log.log(Level.SEVERE, "During the run of the server, a plugin set an excessive velocity on an entity");
log.log(Level.SEVERE, "This may be the cause of the issue, or it may be entirely unrelated");
log.log(Level.SEVERE, org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getMessage());
for (StackTraceElement stack : org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getStackTrace()) {
log.log(Level.SEVERE, "\t\t" + stack);
}
}
// Paper end
} else {
log.log(Level.SEVERE, "The server has not responded for " + earlyWarningEvery / 1000 + " seconds! Creating thread dump");
}
// Paper end - Different message for short timeout
log.log(Level.SEVERE, "------------------------------");
log.log(Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Akarin!):");
log.log(Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):");
dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(MinecraftServer.getServer().primaryThread.getId(), Integer.MAX_VALUE), log);
log.log(Level.SEVERE, "------------------------------");
//
log.log(Level.SEVERE, "Entire Thread Dump:");
ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
for (ThreadInfo thread : threads) {
dumpThread(thread, log);
}
log.log(Level.SEVERE, "------------------------------");
if (restart) RestartCommand.restart(); // GC Inlined
break;
// Paper start - Only print full dump on long timeouts
if (isLongTimeout) {
log.log(Level.SEVERE, "Entire Thread Dump:");
ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
for (ThreadInfo thread : threads) {
dumpThread(thread, log);
}
log.log(Level.SEVERE, "------------------------------");
if (restart) {
RestartCommand.restart();
}
break;
} // Paper end
}
try {
sleep(9000); // Akarin
sleep(1000); // Paper - Reduce check time to every second instead of every ten seconds, more consistent and allows for short timeout
} catch (InterruptedException ex) {
interrupt();
}

View File

@@ -14,9 +14,7 @@ public abstract class MixinAsyncCatcher {
@Overwrite
public static void catchOp(String reason) {
if (enabled) {
if (Akari.isPrimaryThread()) return;
if (enabled && !Akari.isPrimaryThread()) {
if (AkarinGlobalConfig.throwOnAsyncCaught) {
throw new IllegalStateException("Asynchronous " + reason + "!");
} else {

File diff suppressed because it is too large Load Diff

View File

@@ -395,8 +395,8 @@ public class PlayerChunkMap {
// Paper start - Separate into two methods
public void a(int i) {
i = MathHelper.clamp(i, 3, 32);
if (i != this.j) { // Akarin
int j = i - this.j; // Akarin
if (i != this.j) {
int j = i - this.j;
managedPlayersLock.readLock().lock(); // Akarin
ArrayList arraylist = Lists.newArrayList(this.managedPlayers);
managedPlayersLock.readLock().unlock(); // Akarin

View File

@@ -92,6 +92,7 @@ import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.Recipe;
import org.bukkit.inventory.ShapedRecipe;
import org.bukkit.inventory.ShapelessRecipe;
import org.bukkit.loot.LootTable;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.Permission;
import org.bukkit.plugin.Plugin;
@@ -119,7 +120,6 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.MapMaker;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.mojang.authlib.GameProfile;
@@ -1899,6 +1899,13 @@ public final class CraftServer implements Server {
}
}
public LootTable getLootTable(NamespacedKey key) {
Validate.notNull(key, "NamespacedKey cannot be null");
LootTableRegistry registry = getServer().aP(); // PAIL getLootTableRegistry
return new CraftLootTable(key, registry.a(CraftNamespacedKey.toMinecraft(key))); // PAIL rename getLootTable
}
@Deprecated
@Override
public UnsafeValues getUnsafe() {
@@ -1992,17 +1999,20 @@ public final class CraftServer implements Server {
@Override
public void reloadPermissions() {
((SimplePluginManager) pluginManager).clearPermissions();
loadCustomPermissions();
pluginManager.clearPermissions();
if (com.destroystokyo.paper.PaperConfig.loadPermsBeforePlugins) loadCustomPermissions();
for (Plugin plugin : pluginManager.getPlugins()) {
plugin.getDescription().getPermissions().forEach((perm) -> {
for (Permission perm : plugin.getDescription().getPermissions()) {
try {
pluginManager.addPermission(perm);
} catch (IllegalArgumentException ex) {
getLogger().log(Level.WARNING, "Plugin " + plugin.getDescription().getFullName() + " tried to register permission '" + perm.getName() + "' but it's already registered", ex);
}
});
}
}
if (!com.destroystokyo.paper.PaperConfig.loadPermsBeforePlugins) loadCustomPermissions();
DefaultPermissions.registerCorePermissions();
CraftDefaultPermissions.registerCorePermissions();
}
@Override

View File

@@ -5,7 +5,6 @@ import com.destroystokyo.paper.profile.CraftPlayerProfile;
import com.destroystokyo.paper.profile.PlayerProfile;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.io.BaseEncoding;
import com.mojang.authlib.GameProfile;
import io.netty.buffer.Unpooled;
@@ -17,7 +16,6 @@ import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -131,7 +129,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
private boolean hasPlayedBefore = false;
private final ConversationTracker conversationTracker = new ConversationTracker();
private final Set<String> channels = new HashSet<String>();
private final Map<UUID, Set<WeakReference<Plugin>>> hiddenPlayers = Collections.synchronizedMap(new HashMap<>()); // Akarin
private final Map<UUID, Set<WeakReference<Plugin>>> hiddenPlayers = new java.util.concurrent.ConcurrentHashMap<>(); // Akarin
private static final WeakHashMap<Plugin, WeakReference<Plugin>> pluginWeakReferences = new WeakHashMap<>();
private int hash = 0;
private double health = 20;
@@ -1199,14 +1197,12 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
EntityTracker tracker = ((WorldServer) entity.world).tracker;
// Paper end
tracker.entriesLock.updateLock().lock(); // Akarin
tracker.entriesLock.writeLock().lock(); // Akarin
EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
if (entry != null) {
tracker.entriesLock.writeLock().lock(); // Akarin
entry.clear(getHandle());
tracker.entriesLock.writeLock().unlock(); // Akarin
}
tracker.entriesLock.updateLock().unlock(); // Akarin
tracker.entriesLock.writeLock().unlock(); // Akarin
// Remove the hidden player from this player user list, if they're on it
if (other.sentListPacket) {
@@ -1253,14 +1249,12 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, other));
tracker.entriesLock.updateLock().lock(); // Akarin
tracker.entriesLock.writeLock().lock(); // Akarin
EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
if (entry != null && !entry.trackedPlayers.contains(getHandle())) {
tracker.entriesLock.writeLock().lock(); // Akarin
entry.updatePlayer(getHandle());
tracker.entriesLock.writeLock().unlock(); // Akarin
}
tracker.entriesLock.updateLock().unlock(); // Akarin
tracker.entriesLock.writeLock().unlock(); // Akarin
}
// Paper start
private void reregisterPlayer(EntityPlayer player) {
@@ -1871,7 +1865,6 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
public boolean getAffectsSpawning() {
return this.getHandle().affectsSpawning;
}
// Paper end
@Override
public int getViewDistance() {
@@ -1882,6 +1875,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
public void setViewDistance(int viewDistance) {
((WorldServer) getHandle().world).getPlayerChunkMap().updateViewDistance(getHandle(), viewDistance);
}
// Paper end
@Override
public void updateCommands() {
if (getHandle().playerConnection == null) return;
getHandle().server.getCommandDispatcher().a(getHandle());
}
@Override
public void setResourcePack(String url, String hash) {
@@ -2000,4 +2001,4 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
return spigot;
}
// Spigot end
}
}

View File

@@ -17,7 +17,6 @@ import com.destroystokyo.paper.event.executor.asm.ClassDefiner;
import com.google.common.base.Preconditions;
import io.akarin.api.internal.Akari;
import io.akarin.api.internal.utils.thread.SuspendableExecutorCompletionService;
// Paper end
import io.akarin.server.core.AkarinGlobalConfig;