11 Commits

Author SHA1 Message Date
Sotr
10d2d285d9 Upstream Paper 2018-08-19 20:31:26 +08:00
Sotr
818f5559f7 Temp disable timings 2018-08-19 20:25:55 +08:00
Sotr
bf7f6fe3bc Upstream Paper w/ cleanup 2018-08-14 19:39:25 +08:00
Sotr
7acc339704 Safety timings w/ Configurable parallel mode 2018-08-14 19:15:54 +08:00
Sotr
f0d17e54e9 Shared random to world w/ thread safety fixes 2018-08-14 19:05:53 +08:00
Sotr
01c40ed0d3 CircleCI 2.0 w/ Removed unneed volatile and atomic 2018-08-11 03:22:00 +08:00
Sotr
4fdc9f0166 Fully parallel world ticking 2018-08-09 15:16:55 +08:00
Sotr
3b4926bed2 Merge branch 'ver/1.12.2' into parallel-world 2018-08-08 01:30:04 +08:00
Sotr
7e0f44f0af [ci skip] Add missing comment 2018-08-08 01:29:19 +08:00
Sotr
b41e4fd8c3 Resize worlds 2018-08-07 16:56:12 +08:00
Sotr
241a8ffefa Fully parallel entities ticking 2018-08-07 16:41:00 +08:00
30 changed files with 2487 additions and 2894 deletions

50
.circleci/config.yml Normal file
View File

@@ -0,0 +1,50 @@
version: 2
jobs:
build:
working_directory: ~/Akarin-project/Akarin
parallelism: 1
shell: /bin/bash --login
environment:
CIRCLE_ARTIFACTS: /tmp/circleci-artifacts
CIRCLE_TEST_REPORTS: /tmp/circleci-test-results
docker:
- image: circleci/build-image:ubuntu-14.04-XXL-upstart-1189-5614f37
command: /sbin/init
steps:
# Machine Setup
- checkout
# Prepare for artifact
- run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS
- run:
working_directory: ~/Akarin-project/Akarin
command: sudo update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java; sudo update-alternatives --set javac /usr/lib/jvm/java-8-openjdk-amd64/bin/javac; echo -e "export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64" >> $BASH_ENV
# Dependencies
# Restore the dependency cache
- restore_cache:
keys:
# This branch if available
- v1-dep-{{ .Branch }}-
# Default branch if not
- v1-dep-ver/1.12.2-
# Any branch if there are none on the default branch - this should be unnecessary if you have your default branch configured correctly
- v1-dep-
- run: git config --global user.email "circle@circleci.com"
- run: git config --global user.name "CircleCI"
- run: chmod +x scripts/inst.sh
- run: ./scripts/inst.sh --setup --remote
# Save dependency cache
- save_cache:
key: v1-dep-{{ .Branch }}-{{ epoch }}
paths:
- ~/.m2
# Test
- run: yes|cp -rf ./akarin-*.jar $CIRCLE_ARTIFACTS
# Teardown
# Save test results
- store_test_results:
path: /tmp/circleci-test-results
# Save artifacts
- store_artifacts:
path: /tmp/circleci-artifacts
- store_artifacts:
path: /tmp/circleci-test-results

View File

@@ -1,16 +0,0 @@
machine:
java:
version: openjdk8
dependencies:
cache-directories:
- "/home/ubuntu/Akarin/work/Paper/work/Minecraft"
override:
- git config --global user.email "circle@circleci.com"
- git config --global user.name "CircleCI"
- chmod +x scripts/inst.sh
- ./scripts/inst.sh --setup --remote
test:
post:
- yes|cp -rf ./akarin-*.jar $CIRCLE_ARTIFACTS

View File

@@ -1,215 +0,0 @@
/*
* 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 it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.bukkit.Bukkit;
import java.util.logging.Level;
/**
* Akarin Changes Note
* 1) Add volatile to fields (safety issue)
*/
class TimingHandler implements Timing {
private static int idPool = 1;
final int id = idPool++;
final String name;
private final boolean verbose;
private final Int2ObjectOpenHashMap<TimingData> children = new LoadingIntMap<>(TimingData::new);
final TimingData record;
private final TimingHandler groupHandler;
private volatile long start = 0; // Akarin - volatile
private volatile int timingDepth = 0; // Akarin - volatile
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
public Timing startTimingIfSync() {
if (Bukkit.isPrimaryThread()) {
startTiming();
}
return this;
}
@Override
public void stopTimingIfSync() {
if (Bukkit.isPrimaryThread()) {
stopTiming();
}
}
@Override
public Timing startTiming() {
if (enabled && ++timingDepth == 1) {
start = System.nanoTime();
parent = TimingsManager.CURRENT;
TimingsManager.CURRENT = this;
}
return this;
}
@Override
public void stopTiming() {
if (enabled && --timingDepth == 0 && start != 0) {
if (!Bukkit.isPrimaryThread()) {
Bukkit.getLogger().log(Level.SEVERE, "stopTiming called async for " + name);
new Throwable().printStackTrace();
start = 0;
return;
}
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;
}
}

View File

@@ -4,10 +4,12 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import com.google.common.collect.Queues; import com.google.common.collect.Queues;
@@ -15,8 +17,15 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
import co.aikar.timings.Timing; import co.aikar.timings.Timing;
import co.aikar.timings.Timings; import co.aikar.timings.Timings;
import io.akarin.api.internal.Akari.AssignableFactory;
import io.akarin.api.internal.Akari.TimingSignal;
import io.akarin.api.internal.utils.ReentrantSpinningLock;
import io.akarin.api.internal.utils.thread.SuspendableExecutorCompletionService;
import io.akarin.api.internal.utils.thread.SuspendableThreadPoolExecutor;
import io.akarin.server.core.AkarinGlobalConfig; import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.World;
import net.minecraft.server.WorldServer;
@SuppressWarnings("restriction") @SuppressWarnings("restriction")
public abstract class Akari { public abstract class Akari {
@@ -45,19 +54,59 @@ public abstract class Akari {
} }
public static class AssignableFactory implements ThreadFactory { public static class AssignableFactory implements ThreadFactory {
private final String threadName;
private int threadNumber;
public AssignableFactory(String name) {
threadName = name;
}
@Override @Override
public Thread newThread(Runnable run) { public Thread newThread(Runnable run) {
Thread thread = new AssignableThread(run); Thread thread = new AssignableThread(run);
thread.setName("Akarin Parallel Schedule Thread"); thread.setName(StringUtils.replaceChars(threadName, "$", String.valueOf(threadNumber++)));
thread.setPriority(AkarinGlobalConfig.primaryThreadPriority); // Fair thread.setPriority(AkarinGlobalConfig.primaryThreadPriority); // Fair
return thread; return thread;
} }
} }
/** public static class TimingSignal {
* A common tick pool public final World tickedWorld;
*/ public final boolean isEntities;
public static final ExecutorCompletionService<?> STAGE_TICK = new ExecutorCompletionService<>(Executors.newSingleThreadExecutor(new AssignableFactory()));
public TimingSignal(World world, boolean entities) {
tickedWorld = world;
isEntities = entities;
}
}
public static SuspendableExecutorCompletionService<TimingSignal> STAGE_TICK;
static {
resizeTickExecutors(3);
}
public static void resizeTickExecutors(int worlds) {
int parallelism;
switch (AkarinGlobalConfig.parallelMode) {
case -1:
return;
case 0:
parallelism = 2;
break;
case 1:
parallelism = worlds + 1;
break;
case 2:
default:
parallelism = worlds * 2;
break;
}
STAGE_TICK = new SuspendableExecutorCompletionService<>(new SuspendableThreadPoolExecutor(parallelism, parallelism,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
new AssignableFactory("Akarin Parallel Ticking Thread - $")));
}
public static boolean isPrimaryThread() { public static boolean isPrimaryThread() {
return isPrimaryThread(true); return isPrimaryThread(true);
@@ -65,7 +114,7 @@ public abstract class Akari {
public static boolean isPrimaryThread(boolean assign) { public static boolean isPrimaryThread(boolean assign) {
Thread current = Thread.currentThread(); Thread current = Thread.currentThread();
return current == MinecraftServer.getServer().primaryThread || (assign ? current instanceof AssignableThread : false); return current == MinecraftServer.getServer().primaryThread || (assign ? (current.getClass() == AssignableThread.class) : false);
} }
public static final String EMPTY_STRING = ""; public static final String EMPTY_STRING = "";
@@ -97,8 +146,6 @@ public abstract class Akari {
*/ */
public final static Timing worldTiming = getTiming("Akarin - Full World Tick"); public final static Timing worldTiming = getTiming("Akarin - Full World Tick");
public final static Timing entityCallbackTiming = getTiming("Akarin - Entity Parallell Await");
public final static Timing callbackTiming = getTiming("Akarin - Callback Queue"); public final static Timing callbackTiming = getTiming("Akarin - Callback Queue");
private static Timing getTiming(String name) { private static Timing getTiming(String name) {

View File

@@ -1,5 +0,0 @@
package io.akarin.api.internal.mixin;
public interface IMixinLockProvider {
public Object lock();
}

View File

@@ -0,0 +1,8 @@
package io.akarin.api.internal.mixin;
import java.util.Random;
public interface IMixinWorldServer {
public Object lock();
public Random rand();
}

View File

@@ -0,0 +1,102 @@
package io.akarin.api.internal.utils;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public class ReentrantSpinningLock {
/*
* Impl Note:
* A write lock can reentrant as a read lock, while a
* read lock is not allowed to reentrant as a write lock.
* READ LOCK IS UNTESTED, USE WITH CATION.
*/
private final AtomicBoolean writeLocked = new AtomicBoolean(false);
// --------- Thread local restricted fields ---------
private long heldThreadId = 0;
private int reentrantLocks = 0;
/**
* Lock as a typical reentrant write lock
*/
public void lock() {
long currentThreadId = Thread.currentThread().getId();
if (heldThreadId == currentThreadId) {
reentrantLocks++;
} else {
while (!writeLocked.compareAndSet(false, true)) ; // In case acquire one lock concurrently
heldThreadId = currentThreadId;
}
}
public void unlock() {
if (reentrantLocks == 0) {
heldThreadId = 0;
//if (readerThreads.get() == 0 || 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 reentrant read lock
*/
@Deprecated
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
}
}
@Deprecated
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();
}
}
@Deprecated
public class SpinningReadLock {
public void lock() {
lockWeak();
}
public void unlock() {
unlockWeak();
}
}
public SpinningWriteLock writeLock() {
return wrappedWriteLock;
}
public SpinningReadLock readLock() {
return wrappedReadLock;
}
}

View File

@@ -0,0 +1,7 @@
package io.akarin.api.internal.utils.thread;
import java.util.concurrent.ExecutionException;
public class OpenExecutionException extends ExecutionException {
private static final long serialVersionUID = 7830266012832686185L;
}

View File

@@ -0,0 +1,142 @@
/*
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/*
*
*
*
*
*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package io.akarin.api.internal.utils.thread;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeUnit;
public class SuspendableExecutorCompletionService<V> implements CompletionService<V> {
private final SuspendableThreadPoolExecutor executor;
private final BlockingQueue<Future<V>> completionQueue;
public void suspend() {
executor.suspend();
}
public void resume() {
executor.resume();
}
/**
* FutureTask extension to enqueue upon completion
*/
private class QueueingFuture extends FutureTask<Void> {
QueueingFuture(RunnableFuture<V> task) {
super(task, null);
this.task = task;
}
protected void done() { completionQueue.add(task); }
private final Future<V> task;
}
private RunnableFuture<V> newTaskFor(Callable<V> task) {
return new FutureTask<V>(task);
}
private RunnableFuture<V> newTaskFor(Runnable task, V result) {
return new FutureTask<V>(task, result);
}
/**
* Creates an ExecutorCompletionService using the supplied
* executor for base task execution and a
* {@link LinkedBlockingQueue} as a completion queue.
*
* @param executor the executor to use
* @throws NullPointerException if executor is {@code null}
*/
public SuspendableExecutorCompletionService(SuspendableThreadPoolExecutor executor) {
if (executor == null)
throw new NullPointerException();
this.executor = executor;
this.completionQueue = new LinkedBlockingQueue<Future<V>>();
}
/**
* Creates an ExecutorCompletionService using the supplied
* executor for base task execution and the supplied queue as its
* completion queue.
*
* @param executor the executor to use
* @param completionQueue the queue to use as the completion queue
* normally one dedicated for use by this service. This
* queue is treated as unbounded -- failed attempted
* {@code Queue.add} operations for completed tasks cause
* them not to be retrievable.
* @throws NullPointerException if executor or completionQueue are {@code null}
*/
public SuspendableExecutorCompletionService(SuspendableThreadPoolExecutor executor,
BlockingQueue<Future<V>> completionQueue) {
if (executor == null || completionQueue == null)
throw new NullPointerException();
this.executor = executor;
this.completionQueue = completionQueue;
}
public Future<V> submit(Callable<V> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task);
executor.execute(new QueueingFuture(f));
return f;
}
public Future<V> submit(Runnable task, V result) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task, result);
executor.execute(new QueueingFuture(f));
return f;
}
public Future<V> take() throws InterruptedException {
return completionQueue.take();
}
public Future<V> poll() {
return completionQueue.poll();
}
public Future<V> poll(long timeout, TimeUnit unit)
throws InterruptedException {
return completionQueue.poll(timeout, unit);
}
}

View File

@@ -160,16 +160,6 @@ public class AkarinGlobalConfig {
playersPerIOThread = getInt("core.players-per-chunk-io-thread", 50); playersPerIOThread = getInt("core.players-per-chunk-io-thread", 50);
} }
public static boolean silentAsyncTimings;
private static void silentAsyncTimings() {
silentAsyncTimings = getBoolean("core.always-silent-async-timing", false);
}
public static boolean legacyWorldTimings;
private static void legacyWorldTimings() {
legacyWorldTimings = getBoolean("alternative.legacy-world-timings-required", false);
}
public static long timeUpdateInterval; public static long timeUpdateInterval;
private static void timeUpdateInterval() { private static void timeUpdateInterval() {
timeUpdateInterval = getSeconds(getString("core.tick-rate.world-time-update-interval", "1s")) * 10; timeUpdateInterval = getSeconds(getString("core.tick-rate.world-time-update-interval", "1s")) * 10;
@@ -201,52 +191,25 @@ public class AkarinGlobalConfig {
} }
public static String messageKick; public static String messageKick;
private static void messageKick() {
messageKick = getString("messages.disconnect.kick-player", "Kicked by an operator.");
}
public static String messageBan; public static String messageBan;
private static void messageBan() {
messageBan = getString("messages.disconnect.ban-player-name", "You are banned from this server! %s %s");
}
public static String messageBanReason; public static String messageBanReason;
private static void messageBanReason() {
messageBanReason = getString("messages.disconnect.ban-reason", "\nReason: ");
}
public static String messageBanExpires; public static String messageBanExpires;
private static void messageBanExpires() {
messageBanExpires = getString("messages.disconnect.ban-expires", "\nYour ban will be removed on ");
}
public static String messageBanIp; public static String messageBanIp;
private static void messageBanIp() {
messageBanIp = getString("messages.disconnect.ban-player-ip", "Your IP address is banned from this server! %s %s");
}
public static String messageDupLogin; public static String messageDupLogin;
private static void messageDupLogin() {
messageDupLogin = getString("messages.disconnect.kick-player-duplicate-login", "You logged in from another location");
}
public static String messageJoin; public static String messageJoin;
private static void messageJoin() {
messageJoin = getString("messages.connect.player-join-server", "§e%s joined the game");
}
public static String messageJoinRenamed; public static String messageJoinRenamed;
private static void messageJoinRenamed() {
messageJoinRenamed = getString("messages.connect.renamed-player-join-server", "§e%s (formerly known as %s) joined the game");
}
public static String messageKickKeepAlive; public static String messageKickKeepAlive;
private static void messagekickKeepAlive() {
messageKickKeepAlive = getString("messages.disconnect.kick-player-timeout-keep-alive", "Timed out");
}
public static String messagePlayerQuit; public static String messagePlayerQuit;
private static void messagePlayerQuit() { private static void messagekickKeepAlive() {
messageKick = getString("messages.disconnect.kick-player", "Kicked by an operator.");
messageBan = getString("messages.disconnect.ban-player-name", "You are banned from this server! %s %s");
messageBanReason = getString("messages.disconnect.ban-reason", "\nReason: ");
messageBanExpires = getString("messages.disconnect.ban-expires", "\nYour ban will be removed on ");
messageBanIp = getString("messages.disconnect.ban-player-ip", "Your IP address is banned from this server! %s %s");
messageDupLogin = getString("messages.disconnect.kick-player-duplicate-login", "You logged in from another location");
messageJoin = getString("messages.connect.player-join-server", "§e%s joined the game");
messageJoinRenamed = getString("messages.connect.renamed-player-join-server", "§e%s (formerly known as %s) joined the game");
messageKickKeepAlive = getString("messages.disconnect.kick-player-timeout-keep-alive", "Timed out");
messagePlayerQuit = getString("messages.disconnect.player-quit-server", "§e%s left the game"); messagePlayerQuit = getString("messages.disconnect.player-quit-server", "§e%s left the game");
} }
@@ -289,4 +252,9 @@ public class AkarinGlobalConfig {
private static void fileIOThreads() { private static void fileIOThreads() {
fileIOThreads = getInt("core.chunk-save-threads", 2); fileIOThreads = getInt("core.chunk-save-threads", 2);
} }
public static int parallelMode;
private static void parallelMode() {
parallelMode = getInt("core.parallel-mode", 1);
}
} }

View File

@@ -40,6 +40,7 @@ public class AkarinSlackScheduler extends Thread {
MinecraftServer server = MinecraftServer.getServer(); MinecraftServer server = MinecraftServer.getServer();
while (server.isRunning()) { while (server.isRunning()) {
long startProcessTiming = System.currentTimeMillis();
// Send time updates to everyone, it will get the right time from the world the player is in. // Send time updates to everyone, it will get the right time from the world the player is in.
// Time update, from MinecraftServer#D // Time update, from MinecraftServer#D
if (++updateTime >= AkarinGlobalConfig.timeUpdateInterval) { if (++updateTime >= AkarinGlobalConfig.timeUpdateInterval) {
@@ -97,10 +98,9 @@ public class AkarinSlackScheduler extends Thread {
} }
try { try {
Thread.sleep(100); Thread.sleep(100 - (System.currentTimeMillis() - startProcessTiming));
} catch (InterruptedException ex) { } catch (InterruptedException interrupted) {
Akari.logger.warn("Slack scheduler thread was interrupted unexpectly!"); ;
ex.printStackTrace();
} }
} }
} }

View File

@@ -6,7 +6,6 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.CraftServer;
import org.spigotmc.RestartCommand; import org.spigotmc.RestartCommand;
import org.spigotmc.WatchdogThread; import org.spigotmc.WatchdogThread;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
@@ -23,6 +22,10 @@ import net.minecraft.server.MinecraftServer;
public abstract class Watchcat extends Thread { public abstract class Watchcat extends Thread {
@Shadow private static WatchdogThread instance; @Shadow private static WatchdogThread instance;
@Shadow private @Final long timeoutTime; @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 @Final boolean restart;
@Shadow private volatile long lastTick; @Shadow private volatile long lastTick;
@Shadow private volatile boolean stopping; @Shadow private volatile boolean stopping;
@@ -38,48 +41,73 @@ public abstract class Watchcat extends Thread {
@Overwrite @Overwrite
public void run() { public void run() {
while (!stopping) { while (!stopping) {
// // Paper start
if (lastTick != 0 && System.currentTimeMillis() > lastTick + timeoutTime && !Boolean.getBoolean("disable.watchdog")) { // Paper - Add property to disable long currentTime = System.currentTimeMillis();
Logger log = Bukkit.getServer().getLogger(); if ( lastTick != 0 && currentTime > lastTick + earlyWarningEvery && !Boolean.getBoolean("disable.watchdog") )
log.log(Level.SEVERE, "Server has stopped responding!"); {
log.log(Level.SEVERE, "Please report this to https://github.com/Akarin-project/Akarin/issues"); boolean isLongTimeout = currentTime > lastTick + timeoutTime;
log.log(Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports"); // Don't spam early warning dumps
log.log(Level.SEVERE, "Akarin version: " + Bukkit.getServer().getVersion()); if (!isLongTimeout && (earlyWarningEvery <= 0 || !hasStarted || currentTime < lastEarlyWarning + earlyWarningEvery || currentTime < lastTick + earlyWarningDelay))
// continue;
if (net.minecraft.server.World.haveWeSilencedAPhysicsCrash) { lastEarlyWarning = currentTime;
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);
}
}
// Paper end // 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, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH ---"); // Akarin
log.log(Level.SEVERE, "The server has not responded for " + (currentTime - lastTick) / 1000 + " seconds! Creating thread dump");
}
// Paper end - Different message for short timeout
log.log(Level.SEVERE, "------------------------------"); 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 Akarin!):");
dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(MinecraftServer.getServer().primaryThread.getId(), Integer.MAX_VALUE), log); dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(MinecraftServer.getServer().primaryThread.getId(), Integer.MAX_VALUE), log);
log.log(Level.SEVERE, "------------------------------"); log.log(Level.SEVERE, "------------------------------");
// //
log.log(Level.SEVERE, "Entire Thread Dump:"); // Paper start - Only print full dump on long timeouts
ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true); if (isLongTimeout) {
for (ThreadInfo thread : threads) { log.log(Level.SEVERE, "Entire Thread Dump:");
dumpThread(thread, log); ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
for (ThreadInfo thread : threads) {
dumpThread(thread, log);
}
} else {
// log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH ---"); // Akarin
} }
log.log(Level.SEVERE, "------------------------------"); log.log(Level.SEVERE, "------------------------------");
if (restart) RestartCommand.restart(); // GC Inlined if ( isLongTimeout )
break; {
if (restart) {
RestartCommand.restart();
}
break;
} // Paper end
} }
try { 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) { } catch (InterruptedException ex) {
interrupt(); interrupt();
} }

View File

@@ -2,10 +2,14 @@ package io.akarin.server.mixin.core;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask; import java.util.concurrent.FutureTask;
import org.apache.commons.lang.WordUtils;
import org.bukkit.World;
import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor; import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor;
import org.bukkit.event.inventory.InventoryMoveItemEvent; import org.bukkit.event.inventory.InventoryMoveItemEvent;
@@ -22,7 +26,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import co.aikar.timings.MinecraftTimings; import co.aikar.timings.MinecraftTimings;
import io.akarin.api.internal.Akari; import io.akarin.api.internal.Akari;
import io.akarin.api.internal.Akari.AssignableFactory; import io.akarin.api.internal.Akari.AssignableFactory;
import io.akarin.api.internal.mixin.IMixinLockProvider; import io.akarin.api.internal.mixin.IMixinWorldServer;
import io.akarin.server.core.AkarinGlobalConfig; import io.akarin.server.core.AkarinGlobalConfig;
import io.akarin.server.core.AkarinSlackScheduler; import io.akarin.server.core.AkarinSlackScheduler;
import net.minecraft.server.BlockPosition; import net.minecraft.server.BlockPosition;
@@ -41,6 +45,8 @@ import net.minecraft.server.WorldServer;
@Mixin(value = MinecraftServer.class, remap = false) @Mixin(value = MinecraftServer.class, remap = false)
public abstract class MixinMinecraftServer { public abstract class MixinMinecraftServer {
@Shadow @Final public Thread primaryThread; @Shadow @Final public Thread primaryThread;
private boolean tickedPrimaryEntities;
private int cachedWorldSize;
@Overwrite @Overwrite
public String getServerModName() { public String getServerModName() {
@@ -55,6 +61,7 @@ public abstract class MixinMinecraftServer {
private void prerun(CallbackInfo info) { private void prerun(CallbackInfo info) {
primaryThread.setPriority(AkarinGlobalConfig.primaryThreadPriority < Thread.NORM_PRIORITY ? Thread.NORM_PRIORITY : primaryThread.setPriority(AkarinGlobalConfig.primaryThreadPriority < Thread.NORM_PRIORITY ? Thread.NORM_PRIORITY :
(AkarinGlobalConfig.primaryThreadPriority > Thread.MAX_PRIORITY ? 10 : AkarinGlobalConfig.primaryThreadPriority)); (AkarinGlobalConfig.primaryThreadPriority > Thread.MAX_PRIORITY ? 10 : AkarinGlobalConfig.primaryThreadPriority));
Akari.resizeTickExecutors((cachedWorldSize = worlds.size()));
for (int i = 0; i < worlds.size(); ++i) { for (int i = 0; i < worlds.size(); ++i) {
WorldServer world = worlds.get(i); WorldServer world = worlds.get(i);
@@ -108,7 +115,7 @@ public abstract class MixinMinecraftServer {
@Overwrite @Overwrite
protected void l() throws InterruptedException { protected void l() throws InterruptedException {
ExecutorCompletionService<?> executor = new ExecutorCompletionService<>(Executors.newFixedThreadPool(worlds.size(), new AssignableFactory())); ExecutorCompletionService<?> executor = new ExecutorCompletionService<>(Executors.newFixedThreadPool(worlds.size(), new AssignableFactory("Akarin Parallel Terrain Generation Thread - $")));
for (int index = 0; index < worlds.size(); index++) { for (int index = 0; index < worlds.size(); index++) {
WorldServer world = this.worlds.get(index); WorldServer world = this.worlds.get(index);
@@ -147,7 +154,11 @@ public abstract class MixinMinecraftServer {
private boolean tickEntities(WorldServer world) { private boolean tickEntities(WorldServer world) {
try { try {
world.timings.tickEntities.startTiming();
world.tickEntities(); world.tickEntities();
world.timings.tickEntities.stopTiming();
world.getTracker().updatePlayers();
world.explosionDensityCache.clear(); // Paper - Optimize explosions
} catch (Throwable throwable) { } catch (Throwable throwable) {
CrashReport crashreport; CrashReport crashreport;
try { try {
@@ -163,7 +174,9 @@ public abstract class MixinMinecraftServer {
private void tickWorld(WorldServer world) { private void tickWorld(WorldServer world) {
try { try {
world.timings.doTick.startTiming();
world.doTick(); world.doTick();
world.timings.doTick.stopTiming();
} catch (Throwable throwable) { } catch (Throwable throwable) {
CrashReport crashreport; CrashReport crashreport;
try { try {
@@ -177,8 +190,12 @@ public abstract class MixinMinecraftServer {
} }
@Overwrite @Overwrite
public void D() throws InterruptedException { public void D() throws InterruptedException, ExecutionException, CancellationException {
Runnable runnable; Runnable runnable;
Akari.callbackTiming.startTiming();
while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run();
Akari.callbackTiming.stopTiming();
MinecraftTimings.bukkitSchedulerTimer.startTiming(); MinecraftTimings.bukkitSchedulerTimer.startTiming();
this.server.getScheduler().mainThreadHeartbeat(this.ticks); this.server.getScheduler().mainThreadHeartbeat(this.ticks);
MinecraftTimings.bukkitSchedulerTimer.stopTiming(); MinecraftTimings.bukkitSchedulerTimer.stopTiming();
@@ -199,44 +216,86 @@ public abstract class MixinMinecraftServer {
ChunkIOExecutor.tick(); ChunkIOExecutor.tick();
MinecraftTimings.chunkIOTickTimer.stopTiming(); MinecraftTimings.chunkIOTickTimer.stopTiming();
Akari.worldTiming.startTiming(); if (cachedWorldSize != worlds.size()) Akari.resizeTickExecutors((cachedWorldSize = worlds.size()));
if (AkarinGlobalConfig.legacyWorldTimings) { switch (AkarinGlobalConfig.parallelMode) {
for (int i = 0; i < worlds.size(); ++i) { case 1:
WorldServer world = worlds.get(i); case 2:
world.timings.tickEntities.startTiming(); default:
world.timings.doTick.startTiming(); // Never tick one world concurrently!
} for (int i = 0; i < cachedWorldSize; i++) {
} // Impl Note:
Akari.STAGE_TICK.submit(() -> { // Entities ticking: index 1 -> ... -> 0 (parallel)
// Never tick one world concurrently! // World ticking: index 0 -> ... (parallel)
for (int i = 1; i <= worlds.size(); ++i) { int interlace = i + 1;
WorldServer world = worlds.get(i < worlds.size() ? i : 0); WorldServer entityWorld = worlds.get(interlace < cachedWorldSize ? interlace : 0);
synchronized (((IMixinLockProvider) world).lock()) { Akari.STAGE_TICK.submit(() -> {
tickEntities(world); synchronized (((IMixinWorldServer) entityWorld).lock()) {
world.getTracker().updatePlayers(); tickEntities(entityWorld);
world.explosionDensityCache.clear(); // Paper - Optimize explosions }
}, null/*new TimingSignal(entityWorld, true)*/);
if (AkarinGlobalConfig.parallelMode != 1) {
int fi = i;
Akari.STAGE_TICK.submit(() -> {
WorldServer world = worlds.get(fi);
synchronized (((IMixinWorldServer) world).lock()) {
tickWorld(world);
}
}, null);
}
} }
}
}, null); if (AkarinGlobalConfig.parallelMode == 1)
Akari.STAGE_TICK.submit(() -> {
for (int i = 0; i < worlds.size(); ++i) { for (int i = 0; i < cachedWorldSize; i++) {
WorldServer world = worlds.get(i); WorldServer world = worlds.get(i);
synchronized (((IMixinLockProvider) world).lock()) { synchronized (((IMixinWorldServer) world).lock()) {
tickWorld(world); tickWorld(world);
} }
} }
}, null);
Akari.entityCallbackTiming.startTiming();
Akari.STAGE_TICK.take(); for (int i = (AkarinGlobalConfig.parallelMode == 1 ? cachedWorldSize + 1 : cachedWorldSize * 2); i --> 0 ;) {
Akari.entityCallbackTiming.stopTiming(); Akari.STAGE_TICK.take();
}
Akari.worldTiming.stopTiming();
if (AkarinGlobalConfig.legacyWorldTimings) { /* for (int i = (AkarinGlobalConfig.parallelMode == 1 ? cachedWorldSize : cachedWorldSize * 2); i --> 0 ;) {
for (int i = 0; i < worlds.size(); ++i) { long startTiming = System.nanoTime();
WorldServer world = worlds.get(i); TimingSignal signal = Akari.STAGE_TICK.take().get();
world.timings.tickEntities.stopTiming(); IMixinTimingHandler timing = (IMixinTimingHandler) (signal.isEntities ? signal.tickedWorld.timings.tickEntities : signal.tickedWorld.timings.doTick);
world.timings.doTick.stopTiming(); timing.stopTiming(startTiming); // The overlap will be ignored
} } */
break;
case 0:
Akari.STAGE_TICK.submit(() -> {
for (int i = 1; i <= cachedWorldSize; ++i) {
WorldServer world = worlds.get(i < cachedWorldSize ? i : 0);
synchronized (((IMixinWorldServer) world).lock()) {
tickEntities(world);
}
}
}, null);
Akari.STAGE_TICK.submit(() -> {
for (int i = 0; i < cachedWorldSize; ++i) {
WorldServer world = worlds.get(i);
synchronized (((IMixinWorldServer) world).lock()) {
tickWorld(world);
}
}
}, null);
Akari.STAGE_TICK.take();
Akari.STAGE_TICK.take();
break;
case -1:
for (int i = 0; i < cachedWorldSize; ++i) {
WorldServer world = worlds.get(i);
tickWorld(world);
tickEntities(world);
}
break;
} }
Akari.callbackTiming.startTiming(); Akari.callbackTiming.startTiming();

View File

@@ -1,5 +1,7 @@
package io.akarin.server.mixin.core; package io.akarin.server.mixin.core;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level; import java.util.logging.Level;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@@ -7,13 +9,8 @@ import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import co.aikar.timings.Timing; import co.aikar.timings.Timing;
import io.akarin.api.internal.Akari; import io.akarin.api.internal.Akari;
import io.akarin.api.internal.Akari.AssignableThread;
import io.akarin.server.core.AkarinGlobalConfig; import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
@@ -21,26 +18,18 @@ import net.minecraft.server.MinecraftServer;
public abstract class MixinTimingHandler { public abstract class MixinTimingHandler {
@Shadow @Final String name; @Shadow @Final String name;
@Shadow private boolean enabled; @Shadow private boolean enabled;
@Shadow private volatile long start; @Shadow private long start;
@Shadow private volatile int timingDepth; @Shadow private int timingDepth;
@Shadow abstract void addDiff(long diff); @Shadow abstract void addDiff(long diff);
@Shadow public abstract Timing startTiming(); @Shadow public abstract Timing startTiming();
@Overwrite @Overwrite
public Timing startTimingIfSync() { public Timing startTimingIfSync() {
if (Akari.isPrimaryThread(false)) { startTiming();
startTiming();
}
return (Timing) this; return (Timing) this;
} }
@SuppressWarnings({"rawtypes", "unchecked"})
@Inject(method = "startTiming", at = @At("HEAD"), cancellable = true)
public void onStartTiming(CallbackInfoReturnable cir) {
if (!Akari.isPrimaryThread(false)) cir.setReturnValue(this); // Avoid modify any field
}
@Overwrite @Overwrite
public void stopTimingIfSync() { public void stopTimingIfSync() {
if (Akari.isPrimaryThread(false)) { if (Akari.isPrimaryThread(false)) {
@@ -53,20 +42,22 @@ public abstract class MixinTimingHandler {
stopTiming(false); stopTiming(false);
} }
public void stopTiming(long start) {
if (enabled) addDiff(System.nanoTime() - start);
}
public void stopTiming(boolean alreadySync) { public void stopTiming(boolean alreadySync) {
Thread curThread = Thread.currentThread(); if (!enabled || --timingDepth != 0 || start == 0) return;
if (!enabled || curThread instanceof AssignableThread) return; if (!alreadySync) {
if (!alreadySync && curThread != MinecraftServer.getServer().primaryThread) { Thread curThread = Thread.currentThread();
if (AkarinGlobalConfig.silentAsyncTimings) return; if (curThread != MinecraftServer.getServer().primaryThread) {
start = 0;
Bukkit.getLogger().log(Level.SEVERE, "stopTiming called async for " + name); return;
Thread.dumpStack(); }
} }
// Main thread ensured // Safety ensured
if (--timingDepth == 0 && start != 0) { addDiff(System.nanoTime() - start);
addDiff(System.nanoTime() - start); start = 0;
start = 0;
}
} }
} }

View File

@@ -1,15 +1,37 @@
package io.akarin.server.mixin.core; package io.akarin.server.mixin.core;
import java.util.Random;
import org.apache.logging.log4j.LogManager;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import io.akarin.api.internal.mixin.IMixinLockProvider; import io.akarin.api.internal.mixin.IMixinWorldServer;
import net.minecraft.server.WorldServer; import net.minecraft.server.WorldServer;
@Mixin(value = WorldServer.class, remap = false) @Mixin(value = WorldServer.class, remap = false)
public abstract class MixinWorldServer implements IMixinLockProvider { public abstract class MixinWorldServer implements IMixinWorldServer {
private final Object tickLock = new Object(); private final Object tickLock = new Object();
@Override @Override
public Object lock() { public Object lock() {
return tickLock; return tickLock;
} }
private final Random sharedRandom = new io.akarin.api.internal.utils.random.LightRandom() {
private static final long serialVersionUID = 1L;
private boolean locked = false;
@Override
public synchronized void setSeed(long seed) {
if (locked) {
LogManager.getLogger().error("Ignoring setSeed on Entity.SHARED_RANDOM", new Throwable());
} else {
super.setSeed(seed);
locked = true;
}
}
};
@Override
public Random rand() {
return sharedRandom;
}
} }

View File

@@ -27,6 +27,8 @@ import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Vehicle; import org.bukkit.entity.Vehicle;
import co.aikar.timings.MinecraftTimings; // Paper import co.aikar.timings.MinecraftTimings; // Paper
import co.aikar.timings.Timing; // Paper import co.aikar.timings.Timing; // Paper
import io.akarin.api.internal.mixin.IMixinWorldServer;
import org.bukkit.event.entity.EntityCombustByEntityEvent; import org.bukkit.event.entity.EntityCombustByEntityEvent;
import org.bukkit.event.hanging.HangingBreakByEntityEvent; import org.bukkit.event.hanging.HangingBreakByEntityEvent;
import org.bukkit.event.vehicle.VehicleBlockCollisionEvent; import org.bukkit.event.vehicle.VehicleBlockCollisionEvent;
@@ -51,7 +53,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
// CraftBukkit start // CraftBukkit start
private static final int CURRENT_LEVEL = 2; private static final int CURRENT_LEVEL = 2;
// Paper start // Paper start
public static Random SHARED_RANDOM = new io.akarin.api.internal.utils.random.LightRandom() { // Akarin - LightRNG public static Random SHARED_RANDOM = new java.util.Random() {
private boolean locked = false; private boolean locked = false;
@Override @Override
public synchronized void setSeed(long seed) { public synchronized void setSeed(long seed) {
@@ -208,7 +210,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
this.length = 1.8F; this.length = 1.8F;
this.ax = 1; this.ax = 1;
this.ay = 1.0F; this.ay = 1.0F;
this.random = SHARED_RANDOM; // Paper this.random = ((IMixinWorldServer) world).rand(); // Paper // Akarin
this.fireTicks = -this.getMaxFireTicks(); this.fireTicks = -this.getMaxFireTicks();
this.justCreated = true; this.justCreated = true;
this.uniqueID = MathHelper.a(this.random); this.uniqueID = MathHelper.a(this.random);
@@ -1313,6 +1315,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
this.lastYaw -= 360.0F; this.lastYaw -= 360.0F;
} }
world.getChunkAt((int) Math.floor(this.locX) >> 4, (int) Math.floor(this.locZ) >> 4); // Paper - ensure chunk is always loaded
this.setPosition(this.locX, this.locY, this.locZ); this.setPosition(this.locX, this.locY, this.locZ);
this.setYawPitch(f, f1); this.setYawPitch(f, f1);
} }

View File

@@ -33,7 +33,6 @@ import org.bukkit.inventory.MainHand;
/** /**
* Akarin Changes Note * Akarin Changes Note
* 1) Add volatile to fields (time update)
* 2) Add lock to player track (safety issue) * 2) Add lock to player track (safety issue)
*/ */
public class EntityPlayer extends EntityHuman implements ICrafting { public class EntityPlayer extends EntityHuman implements ICrafting {
@@ -1422,8 +1421,8 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
// CraftBukkit start - Add per-player time and weather. // CraftBukkit start - Add per-player time and weather.
public volatile long timeOffset = 0; // Akarin - volatile public long timeOffset = 0;
public volatile boolean relativeTime = true; // Akarin - volatile public boolean relativeTime = true;
public long getPlayerTime() { public long getPlayerTime() {
if (this.relativeTime) { if (this.relativeTime) {

View File

@@ -25,7 +25,7 @@ public class EntityTracker {
private final Set<EntityTrackerEntry> c = Sets.newHashSet(); private final Set<EntityTrackerEntry> c = Sets.newHashSet();
public final ReentrantReadWriteUpdateLock entriesLock = new ReentrantReadWriteUpdateLock(); // Akarin - add lock public final ReentrantReadWriteUpdateLock entriesLock = new ReentrantReadWriteUpdateLock(); // Akarin - add lock
public final IntHashMap<EntityTrackerEntry> trackedEntities = new IntHashMap(); public final IntHashMap<EntityTrackerEntry> trackedEntities = new IntHashMap();
private volatile int e; // Akarin - volatile private int e;
public EntityTracker(WorldServer worldserver) { public EntityTracker(WorldServer worldserver) {
this.world = worldserver; this.world = worldserver;

File diff suppressed because it is too large Load Diff

View File

@@ -32,7 +32,6 @@ import org.apache.logging.log4j.MarkerManager;
/** /**
* Akarin Changes Note * Akarin Changes Note
* 1) Add volatile to fields (nsc)
* 2) Expose private members (nsc) * 2) Expose private members (nsc)
* 3) Changes lock type to updatable lock (compatibility) * 3) Changes lock type to updatable lock (compatibility)
* 4) Removes unneed array creation (performance) * 4) Removes unneed array creation (performance)
@@ -81,7 +80,7 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
public SocketAddress l; public SocketAddress l;
public java.util.UUID spoofedUUID; public java.util.UUID spoofedUUID;
public com.mojang.authlib.properties.Property[] spoofedProfile; public com.mojang.authlib.properties.Property[] spoofedProfile;
public volatile boolean preparing = true; // Akarin - add volatile public boolean preparing = true;
// Spigot End // Spigot End
private PacketListener m; private PacketListener m;
private IChatBaseComponent n; private IChatBaseComponent n;

View File

@@ -106,7 +106,7 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
packetdataserializer.writeBoolean(this.f); packetdataserializer.writeBoolean(this.f);
packetdataserializer.d(this.c); packetdataserializer.d(this.c);
packetdataserializer.d(this.d.array().length); // Akarin packetdataserializer.d(this.d.array().length); // Akarin
packetdataserializer.writeBytes(this.d.array()); packetdataserializer.writeBytes(this.d.array()); // Akarin
packetdataserializer.d(this.e.size()); packetdataserializer.d(this.e.size());
Iterator iterator = this.e.iterator(); Iterator iterator = this.e.iterator();

View File

@@ -58,10 +58,10 @@ public class PlayerChunkMap {
private final List<PlayerChunk> g = Lists.newLinkedList(); private final List<PlayerChunk> g = Lists.newLinkedList();
private final List<PlayerChunk> h = Lists.newLinkedList(); private final List<PlayerChunk> h = Lists.newLinkedList();
private final List<PlayerChunk> i = Lists.newCopyOnWriteArrayList(); // Akarin - bad plugin will access this private final List<PlayerChunk> i = Lists.newCopyOnWriteArrayList(); // Akarin - bad plugin will access this
private AtomicInteger j = new AtomicInteger(); public int getViewDistance() { return j.get(); } // Paper OBFHELPER // Akarin - atmoic private int j; public int getViewDistance() { return j; } // Paper OBFHELPER
private long k; private long k;
private AtomicBoolean l = new AtomicBoolean(true); // Akarin - atmoic private boolean l = true;
private AtomicBoolean m = new AtomicBoolean(true); // Akarin - atmoic private boolean m = true;
private boolean wasNotEmpty; // CraftBukkit - add field private boolean wasNotEmpty; // CraftBukkit - add field
public PlayerChunkMap(WorldServer worldserver) { public PlayerChunkMap(WorldServer worldserver) {
@@ -142,8 +142,8 @@ public class PlayerChunkMap {
} // Paper timing } // Paper timing
} }
if (this.l.get() && i % 4L == 0L) { if (this.l && i % 4L == 0L) {
this.l.getAndSet(false); this.l = false;
try (Timing ignored = world.timings.doChunkMapSortMissing.startTiming()) { // Paper try (Timing ignored = world.timings.doChunkMapSortMissing.startTiming()) { // Paper
Collections.sort(this.h, new Comparator() { Collections.sort(this.h, new Comparator() {
public int a(PlayerChunk playerchunk, PlayerChunk playerchunk1) { public int a(PlayerChunk playerchunk, PlayerChunk playerchunk1) {
@@ -157,8 +157,8 @@ public class PlayerChunkMap {
} // Paper timing } // Paper timing
} }
if (this.m.get() && i % 4L == 2L) { if (this.m && i % 4L == 2L) {
this.m.getAndSet(false); this.m = false;
try (Timing ignored = world.timings.doChunkMapSortSendToPlayers.startTiming()) { // Paper try (Timing ignored = world.timings.doChunkMapSortSendToPlayers.startTiming()) { // Paper
Collections.sort(this.g, new Comparator() { Collections.sort(this.g, new Comparator() {
public int a(PlayerChunk playerchunk, PlayerChunk playerchunk1) { public int a(PlayerChunk playerchunk, PlayerChunk playerchunk1) {
@@ -425,8 +425,8 @@ public class PlayerChunkMap {
// Paper start - Separate into two methods // Paper start - Separate into two methods
public void a(int i) { public void a(int i) {
i = MathHelper.clamp(i, 3, 32); i = MathHelper.clamp(i, 3, 32);
if (i != this.j.get()) { // Akarin - atmoic if (i != this.j) {
int j = i - this.j.get(); // Akarin - atmoic int j = i - this.j;
managedPlayersLock.readLock().lock(); // Akarin managedPlayersLock.readLock().lock(); // Akarin
ArrayList arraylist = Lists.newArrayList(this.managedPlayers); ArrayList arraylist = Lists.newArrayList(this.managedPlayers);
managedPlayersLock.readLock().unlock(); // Akarin managedPlayersLock.readLock().unlock(); // Akarin
@@ -437,7 +437,7 @@ public class PlayerChunkMap {
this.setViewDistance(entityplayer, i, false); // Paper - Split, don't mark sort pending, we'll handle it after this.setViewDistance(entityplayer, i, false); // Paper - Split, don't mark sort pending, we'll handle it after
} }
this.j.getAndSet(i); // Akarin - atmoic this.j = i;
this.e(); this.e();
} }
} }
@@ -489,8 +489,8 @@ public class PlayerChunkMap {
// Paper end // Paper end
private void e() { private void e() {
this.l.getAndSet(true); // Akarin - atmoic this.l = true;
this.m.getAndSet(true); // Akarin - atmoic this.m = true;
} }
public static int getFurthestViewableBlock(int i) { public static int getFurthestViewableBlock(int i) {

View File

@@ -75,9 +75,9 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
private final MinecraftServer minecraftServer; private final MinecraftServer minecraftServer;
public EntityPlayer player; public EntityPlayer player;
private int e; private int e;
private volatile long f = getCurrentMillis(); public void setLastPing(long lastPing) { this.f = lastPing;}; public long getLastPing() { return this.f;}; // Paper - OBFHELPER - set ping to delay initial // Akarin - private -> public - volatile private long f = getCurrentMillis(); public void setLastPing(long lastPing) { this.f = lastPing;}; public long getLastPing() { return this.f;}; // Paper - OBFHELPER - set ping to delay initial // Akarin - private -> public
private volatile boolean g; public void setPendingPing(boolean isPending) { this.g = isPending;}; public boolean isPendingPing() { return this.g;}; // Paper - OBFHELPER // Akarin - private -> public - volatile private boolean g; public void setPendingPing(boolean isPending) { this.g = isPending;}; public boolean isPendingPing() { return this.g;}; // Paper - OBFHELPER // Akarin - private -> public
private volatile long h; public void setKeepAliveID(long keepAliveID) { this.h = keepAliveID;}; public long getKeepAliveID() {return this.h; }; // Paper - OBFHELPER // Akarin - private -> public - volatile private long h; public void setKeepAliveID(long keepAliveID) { this.h = keepAliveID;}; public long getKeepAliveID() {return this.h; }; // Paper - OBFHELPER // Akarin - private -> public
// CraftBukkit start - multithreaded fields // CraftBukkit start - multithreaded fields
private volatile int chatThrottle; private volatile int chatThrottle;
private static final AtomicIntegerFieldUpdater chatSpamField = AtomicIntegerFieldUpdater.newUpdater(PlayerConnection.class, "chatThrottle"); private static final AtomicIntegerFieldUpdater chatSpamField = AtomicIntegerFieldUpdater.newUpdater(PlayerConnection.class, "chatThrottle");

View File

@@ -1252,8 +1252,25 @@ public abstract class PlayerList {
} }
public void sendPacketNearby(@Nullable EntityHuman entityhuman, double d0, double d1, double d2, double d3, int i, Packet<?> packet) { public void sendPacketNearby(@Nullable EntityHuman entityhuman, double d0, double d1, double d2, double d3, int i, Packet<?> packet) {
for (int j = 0; j < this.players.size(); ++j) { // Paper start - Use world list instead of server list where preferable
EntityPlayer entityplayer = this.players.get(j); sendPacketNearby(entityhuman, d0, d1, d2, d3, i, null, packet); // Retained for compatibility
}
public void sendPacketNearby(@Nullable EntityHuman entityhuman, double d0, double d1, double d2, double d3, WorldServer world, Packet<?> packet) {
sendPacketNearby(entityhuman, d0, d1, d2, d3, world.dimension, world, packet);
}
public void sendPacketNearby(@Nullable EntityHuman entityhuman, double d0, double d1, double d2, double d3, int i, @Nullable WorldServer world, Packet<?> packet) {
if (world == null && entityhuman != null && entityhuman.world instanceof WorldServer) {
world = (WorldServer) entityhuman.world;
}
List<? extends EntityHuman> players1 = world == null ? players : world.players;
for (int j = 0; j < players1.size(); ++j) {
EntityHuman entity = players1.get(j);
if (!(entity instanceof EntityPlayer)) continue;
EntityPlayer entityplayer = (EntityPlayer) players1.get(j);
// Paper end
// CraftBukkit start - Test if player receiving packet can see the source of the packet // CraftBukkit start - Test if player receiving packet can see the source of the packet
if (entityhuman != null && entityhuman instanceof EntityPlayer && !entityplayer.getBukkitEntity().canSee(((EntityPlayer) entityhuman).getBukkitEntity())) { if (entityhuman != null && entityhuman instanceof EntityPlayer && !entityplayer.getBukkitEntity().canSee(((EntityPlayer) entityhuman).getBukkitEntity())) {
@@ -1261,7 +1278,7 @@ public abstract class PlayerList {
} }
// CraftBukkit end // CraftBukkit end
if (entityplayer != entityhuman && entityplayer.dimension == i) { if (entityplayer != entityhuman && (world != null || entityplayer.dimension == i)) { // Paper
double d4 = d0 - entityplayer.locX; double d4 = d0 - entityplayer.locX;
double d5 = d1 - entityplayer.locY; double d5 = d1 - entityplayer.locY;
double d6 = d2 - entityplayer.locZ; double d6 = d2 - entityplayer.locZ;

View File

@@ -9,7 +9,7 @@ import javax.annotation.Nullable;
/** /**
* Akarin Changes Note * Akarin Changes Note
* 1) FastBitSet for faster access (performance) * 1) BitSet for faster access (performance)
*/ */
public class RegistryID<K> implements Registry { public class RegistryID<K> implements Registry {

View File

@@ -1,773 +0,0 @@
package net.minecraft.server;
import com.google.common.collect.Maps;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.Nullable;
// CraftBukkit start
import org.bukkit.Bukkit;
import org.bukkit.event.weather.ThunderChangeEvent;
import org.bukkit.event.weather.WeatherChangeEvent;
// CraftBukkit end
/**
* Akarin Changes Note
* 1) Add volatile to fields (slack service)
*/
public class WorldData {
private String b;
private int c;
private boolean d;
public static final EnumDifficulty a = EnumDifficulty.NORMAL;
private long e;
private WorldType f;
private String g;
private int h;
private int i;
private int j;
private volatile long k; // Akarin - volatile - OBFHELPER: time
private volatile long l; // Akarin - volatile - OBFHELPER: dayTime
private long m;
private long n;
private NBTTagCompound o;
private int p;
private String levelName;
private int r;
private int s;
private boolean t;
private int u;
private boolean v;
private int w;
private EnumGamemode x;
private boolean y;
private boolean z;
private boolean A;
private boolean B;
private volatile EnumDifficulty C; // Akarin - volatile
private boolean D;
private double E;
private double F;
private double G;
private long H;
private double I;
private double J;
private double K;
private int L;
private int M;
private final Map<DimensionManager, NBTTagCompound> N;
private GameRules O;
public WorldServer world; // CraftBukkit
protected WorldData() {
this.f = WorldType.NORMAL;
this.g = "";
this.G = 6.0E7D;
this.J = 5.0D;
this.K = 0.2D;
this.L = 5;
this.M = 15;
this.N = Maps.newEnumMap(DimensionManager.class);
this.O = new GameRules();
}
public static void a(DataConverterManager dataconvertermanager) {
dataconvertermanager.a(DataConverterTypes.LEVEL, new DataInspector() {
@Override
public NBTTagCompound a(DataConverter dataconverter, NBTTagCompound nbttagcompound, int i) {
if (nbttagcompound.hasKeyOfType("Player", 10)) {
nbttagcompound.set("Player", dataconverter.a(DataConverterTypes.PLAYER, nbttagcompound.getCompound("Player"), i));
}
return nbttagcompound;
}
});
}
public WorldData(NBTTagCompound nbttagcompound) {
this.f = WorldType.NORMAL;
this.g = "";
this.G = 6.0E7D;
this.J = 5.0D;
this.K = 0.2D;
this.L = 5;
this.M = 15;
this.N = Maps.newEnumMap(DimensionManager.class);
this.O = new GameRules();
NBTTagCompound nbttagcompound1;
if (nbttagcompound.hasKeyOfType("Version", 10)) {
nbttagcompound1 = nbttagcompound.getCompound("Version");
this.b = nbttagcompound1.getString("Name");
this.c = nbttagcompound1.getInt("Id");
this.d = nbttagcompound1.getBoolean("Snapshot");
}
this.e = nbttagcompound.getLong("RandomSeed");
if (nbttagcompound.hasKeyOfType("generatorName", 8)) {
String s = nbttagcompound.getString("generatorName");
this.f = WorldType.getType(s);
if (this.f == null) {
this.f = WorldType.NORMAL;
} else if (this.f.f()) {
int i = 0;
if (nbttagcompound.hasKeyOfType("generatorVersion", 99)) {
i = nbttagcompound.getInt("generatorVersion");
}
this.f = this.f.a(i);
}
if (nbttagcompound.hasKeyOfType("generatorOptions", 8)) {
this.g = nbttagcompound.getString("generatorOptions");
}
}
this.x = EnumGamemode.getById(nbttagcompound.getInt("GameType"));
if (nbttagcompound.hasKeyOfType("MapFeatures", 99)) {
this.y = nbttagcompound.getBoolean("MapFeatures");
} else {
this.y = true;
}
this.h = nbttagcompound.getInt("SpawnX");
this.i = nbttagcompound.getInt("SpawnY");
this.j = nbttagcompound.getInt("SpawnZ");
this.k = nbttagcompound.getLong("Time");
if (nbttagcompound.hasKeyOfType("DayTime", 99)) {
this.l = nbttagcompound.getLong("DayTime");
} else {
this.l = this.k;
}
this.m = nbttagcompound.getLong("LastPlayed");
this.n = nbttagcompound.getLong("SizeOnDisk");
this.levelName = nbttagcompound.getString("LevelName");
this.r = nbttagcompound.getInt("version");
this.s = nbttagcompound.getInt("clearWeatherTime");
this.u = nbttagcompound.getInt("rainTime");
this.t = nbttagcompound.getBoolean("raining");
this.w = nbttagcompound.getInt("thunderTime");
this.v = nbttagcompound.getBoolean("thundering");
this.z = nbttagcompound.getBoolean("hardcore");
if (nbttagcompound.hasKeyOfType("initialized", 99)) {
this.B = nbttagcompound.getBoolean("initialized");
} else {
this.B = true;
}
if (nbttagcompound.hasKeyOfType("allowCommands", 99)) {
this.A = nbttagcompound.getBoolean("allowCommands");
} else {
this.A = this.x == EnumGamemode.CREATIVE;
}
if (nbttagcompound.hasKeyOfType("Player", 10)) {
this.o = nbttagcompound.getCompound("Player");
this.p = this.o.getInt("Dimension");
}
if (nbttagcompound.hasKeyOfType("GameRules", 10)) {
this.O.a(nbttagcompound.getCompound("GameRules"));
}
if (nbttagcompound.hasKeyOfType("Difficulty", 99)) {
this.C = EnumDifficulty.getById(nbttagcompound.getByte("Difficulty"));
}
if (nbttagcompound.hasKeyOfType("DifficultyLocked", 1)) {
this.D = nbttagcompound.getBoolean("DifficultyLocked");
}
if (nbttagcompound.hasKeyOfType("BorderCenterX", 99)) {
this.E = nbttagcompound.getDouble("BorderCenterX");
}
if (nbttagcompound.hasKeyOfType("BorderCenterZ", 99)) {
this.F = nbttagcompound.getDouble("BorderCenterZ");
}
if (nbttagcompound.hasKeyOfType("BorderSize", 99)) {
this.G = nbttagcompound.getDouble("BorderSize");
}
if (nbttagcompound.hasKeyOfType("BorderSizeLerpTime", 99)) {
this.H = nbttagcompound.getLong("BorderSizeLerpTime");
}
if (nbttagcompound.hasKeyOfType("BorderSizeLerpTarget", 99)) {
this.I = nbttagcompound.getDouble("BorderSizeLerpTarget");
}
if (nbttagcompound.hasKeyOfType("BorderSafeZone", 99)) {
this.J = nbttagcompound.getDouble("BorderSafeZone");
}
if (nbttagcompound.hasKeyOfType("BorderDamagePerBlock", 99)) {
this.K = nbttagcompound.getDouble("BorderDamagePerBlock");
}
if (nbttagcompound.hasKeyOfType("BorderWarningBlocks", 99)) {
this.L = nbttagcompound.getInt("BorderWarningBlocks");
}
if (nbttagcompound.hasKeyOfType("BorderWarningTime", 99)) {
this.M = nbttagcompound.getInt("BorderWarningTime");
}
if (nbttagcompound.hasKeyOfType("DimensionData", 10)) {
nbttagcompound1 = nbttagcompound.getCompound("DimensionData");
Iterator iterator = nbttagcompound1.c().iterator();
while (iterator.hasNext()) {
String s1 = (String) iterator.next();
this.N.put(DimensionManager.a(Integer.parseInt(s1)), nbttagcompound1.getCompound(s1));
}
}
}
public WorldData(WorldSettings worldsettings, String s) {
this.f = WorldType.NORMAL;
this.g = "";
this.G = 6.0E7D;
this.J = 5.0D;
this.K = 0.2D;
this.L = 5;
this.M = 15;
this.N = Maps.newEnumMap(DimensionManager.class);
this.O = new GameRules();
this.a(worldsettings);
this.levelName = s;
this.C = WorldData.a;
this.B = false;
}
public void a(WorldSettings worldsettings) {
this.e = worldsettings.d();
this.x = worldsettings.e();
this.y = worldsettings.g();
this.z = worldsettings.f();
this.f = worldsettings.h();
this.g = worldsettings.j();
this.A = worldsettings.i();
}
public WorldData(WorldData worlddata) {
this.f = WorldType.NORMAL;
this.g = "";
this.G = 6.0E7D;
this.J = 5.0D;
this.K = 0.2D;
this.L = 5;
this.M = 15;
this.N = Maps.newEnumMap(DimensionManager.class);
this.O = new GameRules();
this.e = worlddata.e;
this.f = worlddata.f;
this.g = worlddata.g;
this.x = worlddata.x;
this.y = worlddata.y;
this.h = worlddata.h;
this.i = worlddata.i;
this.j = worlddata.j;
this.k = worlddata.k;
this.l = worlddata.l;
this.m = worlddata.m;
this.n = worlddata.n;
this.o = worlddata.o;
this.p = worlddata.p;
this.levelName = worlddata.levelName;
this.r = worlddata.r;
this.u = worlddata.u;
this.t = worlddata.t;
this.w = worlddata.w;
this.v = worlddata.v;
this.z = worlddata.z;
this.A = worlddata.A;
this.B = worlddata.B;
this.O = worlddata.O;
this.C = worlddata.C;
this.D = worlddata.D;
this.E = worlddata.E;
this.F = worlddata.F;
this.G = worlddata.G;
this.H = worlddata.H;
this.I = worlddata.I;
this.J = worlddata.J;
this.K = worlddata.K;
this.M = worlddata.M;
this.L = worlddata.L;
}
public NBTTagCompound a(@Nullable NBTTagCompound nbttagcompound) {
if (nbttagcompound == null) {
nbttagcompound = this.o;
}
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
this.a(nbttagcompound1, nbttagcompound);
return nbttagcompound1;
}
private void a(NBTTagCompound nbttagcompound, NBTTagCompound nbttagcompound1) {
NBTTagCompound nbttagcompound2 = new NBTTagCompound();
nbttagcompound2.setString("Name", "1.12.2");
nbttagcompound2.setInt("Id", 1343);
nbttagcompound2.setBoolean("Snapshot", false);
nbttagcompound.set("Version", nbttagcompound2);
nbttagcompound.setInt("DataVersion", 1343);
nbttagcompound.setLong("RandomSeed", this.e);
nbttagcompound.setString("generatorName", this.f.name());
nbttagcompound.setInt("generatorVersion", this.f.getVersion());
nbttagcompound.setString("generatorOptions", this.g);
nbttagcompound.setInt("GameType", this.x.getId());
nbttagcompound.setBoolean("MapFeatures", this.y);
nbttagcompound.setInt("SpawnX", this.h);
nbttagcompound.setInt("SpawnY", this.i);
nbttagcompound.setInt("SpawnZ", this.j);
nbttagcompound.setLong("Time", this.k);
nbttagcompound.setLong("DayTime", this.l);
nbttagcompound.setLong("SizeOnDisk", this.n);
nbttagcompound.setLong("LastPlayed", MinecraftServer.aw());
nbttagcompound.setString("LevelName", this.levelName);
nbttagcompound.setInt("version", this.r);
nbttagcompound.setInt("clearWeatherTime", this.s);
nbttagcompound.setInt("rainTime", this.u);
nbttagcompound.setBoolean("raining", this.t);
nbttagcompound.setInt("thunderTime", this.w);
nbttagcompound.setBoolean("thundering", this.v);
nbttagcompound.setBoolean("hardcore", this.z);
nbttagcompound.setBoolean("allowCommands", this.A);
nbttagcompound.setBoolean("initialized", this.B);
nbttagcompound.setDouble("BorderCenterX", this.E);
nbttagcompound.setDouble("BorderCenterZ", this.F);
nbttagcompound.setDouble("BorderSize", this.G);
nbttagcompound.setLong("BorderSizeLerpTime", this.H);
nbttagcompound.setDouble("BorderSafeZone", this.J);
nbttagcompound.setDouble("BorderDamagePerBlock", this.K);
nbttagcompound.setDouble("BorderSizeLerpTarget", this.I);
nbttagcompound.setDouble("BorderWarningBlocks", this.L);
nbttagcompound.setDouble("BorderWarningTime", this.M);
if (this.C != null) {
nbttagcompound.setByte("Difficulty", (byte) this.C.a());
}
nbttagcompound.setBoolean("DifficultyLocked", this.D);
nbttagcompound.set("GameRules", this.O.a());
NBTTagCompound nbttagcompound3 = new NBTTagCompound();
Iterator iterator = this.N.entrySet().iterator();
while (iterator.hasNext()) {
Entry entry = (Entry) iterator.next();
nbttagcompound3.set(String.valueOf(((DimensionManager) entry.getKey()).getDimensionID()), (NBTBase) entry.getValue());
}
nbttagcompound.set("DimensionData", nbttagcompound3);
if (nbttagcompound1 != null) {
nbttagcompound.set("Player", nbttagcompound1);
}
}
public long getSeed() {
return this.e;
}
public int b() {
return this.h;
}
public int c() {
return this.i;
}
public int d() {
return this.j;
}
public long getTime() {
return this.k;
}
public long getDayTime() {
return this.l;
}
public NBTTagCompound h() {
return this.o;
}
public void setTime(long i) {
this.k = i;
}
public void setDayTime(long i) {
this.l = i;
}
public void setSpawn(BlockPosition blockposition) {
this.h = blockposition.getX();
this.i = blockposition.getY();
this.j = blockposition.getZ();
}
public String getName() {
return this.levelName;
}
public void a(String s) {
this.levelName = s;
}
public int k() {
return this.r;
}
public void e(int i) {
this.r = i;
}
public int z() {
return this.s;
}
public void i(int i) {
this.s = i;
}
public boolean isThundering() {
return this.v;
}
public void setThundering(boolean flag) {
// CraftBukkit start
org.bukkit.World world = Bukkit.getWorld(getName());
if (world != null) {
ThunderChangeEvent thunder = new ThunderChangeEvent(world, flag);
Bukkit.getServer().getPluginManager().callEvent(thunder);
if (thunder.isCancelled()) {
return;
}
}
// CraftBukkit end
this.v = flag;
}
public int getThunderDuration() {
return this.w;
}
public void setThunderDuration(int i) {
this.w = i;
}
public boolean hasStorm() {
return this.t;
}
public void setStorm(boolean flag) {
// CraftBukkit start
org.bukkit.World world = Bukkit.getWorld(getName());
if (world != null) {
WeatherChangeEvent weather = new WeatherChangeEvent(world, flag);
Bukkit.getServer().getPluginManager().callEvent(weather);
if (weather.isCancelled()) {
return;
}
}
// CraftBukkit end
this.t = flag;
}
public int getWeatherDuration() {
return this.u;
}
public void setWeatherDuration(int i) {
this.u = i;
}
public EnumGamemode getGameType() {
return this.x;
}
public boolean shouldGenerateMapFeatures() {
return this.y;
}
public void f(boolean flag) {
this.y = flag;
}
public void setGameType(EnumGamemode enumgamemode) {
this.x = enumgamemode;
}
public boolean isHardcore() {
return this.z;
}
public void g(boolean flag) {
this.z = flag;
}
public WorldType getType() {
return this.f;
}
public void a(WorldType worldtype) {
this.f = worldtype;
}
public String getGeneratorOptions() {
return this.g == null ? "" : this.g;
}
public boolean u() {
return this.A;
}
public void c(boolean flag) {
this.A = flag;
}
public boolean v() {
return this.B;
}
public void d(boolean flag) {
this.B = flag;
}
public GameRules w() {
return this.O;
}
public double B() {
return this.E;
}
public double C() {
return this.F;
}
public double D() {
return this.G;
}
public void a(double d0) {
this.G = d0;
}
public long E() {
return this.H;
}
public void e(long i) {
this.H = i;
}
public double F() {
return this.I;
}
public void b(double d0) {
this.I = d0;
}
public void c(double d0) {
this.F = d0;
}
public void d(double d0) {
this.E = d0;
}
public double G() {
return this.J;
}
public void e(double d0) {
this.J = d0;
}
public double H() {
return this.K;
}
public void f(double d0) {
this.K = d0;
}
public int I() {
return this.L;
}
public int J() {
return this.M;
}
public void j(int i) {
this.L = i;
}
public void k(int i) {
this.M = i;
}
public EnumDifficulty getDifficulty() {
return this.C;
}
public void setDifficulty(EnumDifficulty enumdifficulty) {
this.C = enumdifficulty;
// CraftBukkit start
PacketPlayOutServerDifficulty packet = new PacketPlayOutServerDifficulty(this.getDifficulty(), this.isDifficultyLocked());
for (EntityPlayer player : (java.util.List<EntityPlayer>) (java.util.List) world.players) {
player.playerConnection.sendPacket(packet);
}
// CraftBukkit end
}
public boolean isDifficultyLocked() {
return this.D;
}
public void e(boolean flag) {
this.D = flag;
}
public void a(CrashReportSystemDetails crashreportsystemdetails) {
crashreportsystemdetails.a("Level seed", new CrashReportCallable() {
public String a() throws Exception {
return String.valueOf(WorldData.this.getSeed());
}
@Override
public Object call() throws Exception {
return this.a();
}
});
crashreportsystemdetails.a("Level generator", new CrashReportCallable() {
public String a() throws Exception {
return String.format("ID %02d - %s, ver %d. Features enabled: %b", new Object[] { Integer.valueOf(WorldData.this.f.g()), WorldData.this.f.name(), Integer.valueOf(WorldData.this.f.getVersion()), Boolean.valueOf(WorldData.this.y)});
}
@Override
public Object call() throws Exception {
return this.a();
}
});
crashreportsystemdetails.a("Level generator options", new CrashReportCallable() {
public String a() throws Exception {
return WorldData.this.g;
}
@Override
public Object call() throws Exception {
return this.a();
}
});
crashreportsystemdetails.a("Level spawn location", new CrashReportCallable() {
public String a() throws Exception {
return CrashReportSystemDetails.a(WorldData.this.h, WorldData.this.i, WorldData.this.j);
}
@Override
public Object call() throws Exception {
return this.a();
}
});
crashreportsystemdetails.a("Level time", new CrashReportCallable() {
public String a() throws Exception {
return String.format("%d game time, %d day time", new Object[] { Long.valueOf(WorldData.this.k), Long.valueOf(WorldData.this.l)});
}
@Override
public Object call() throws Exception {
return this.a();
}
});
crashreportsystemdetails.a("Level dimension", new CrashReportCallable() {
public String a() throws Exception {
return String.valueOf(WorldData.this.p);
}
@Override
public Object call() throws Exception {
return this.a();
}
});
crashreportsystemdetails.a("Level storage version", new CrashReportCallable() {
public String a() throws Exception {
String s = "Unknown?";
try {
switch (WorldData.this.r) {
case 19132:
s = "McRegion";
break;
case 19133:
s = "Anvil";
}
} catch (Throwable throwable) {
;
}
return String.format("0x%05X - %s", new Object[] { Integer.valueOf(WorldData.this.r), s});
}
@Override
public Object call() throws Exception {
return this.a();
}
});
crashreportsystemdetails.a("Level weather", new CrashReportCallable() {
public String a() throws Exception {
return String.format("Rain time: %d (now: %b), thunder time: %d (now: %b)", new Object[] { Integer.valueOf(WorldData.this.u), Boolean.valueOf(WorldData.this.t), Integer.valueOf(WorldData.this.w), Boolean.valueOf(WorldData.this.v)});
}
@Override
public Object call() throws Exception {
return this.a();
}
});
crashreportsystemdetails.a("Level game mode", new CrashReportCallable() {
public String a() throws Exception {
return String.format("Game mode: %s (ID %d). Hardcore: %b. Cheats: %b", new Object[] { WorldData.this.x.b(), Integer.valueOf(WorldData.this.x.getId()), Boolean.valueOf(WorldData.this.z), Boolean.valueOf(WorldData.this.A)});
}
@Override
public Object call() throws Exception {
return this.a();
}
});
}
public NBTTagCompound a(DimensionManager dimensionmanager) {
NBTTagCompound nbttagcompound = this.N.get(dimensionmanager);
return nbttagcompound == null ? new NBTTagCompound() : nbttagcompound;
}
public void a(DimensionManager dimensionmanager, NBTTagCompound nbttagcompound) {
this.N.put(dimensionmanager, nbttagcompound);
}
// CraftBukkit start - Check if the name stored in NBT is the correct one
public void checkName( String name ) {
if ( !this.levelName.equals( name ) ) {
this.levelName = name;
}
}
// CraftBukkit end
}

View File

@@ -1291,7 +1291,7 @@ public class WorldServer extends World implements IAsyncTaskHandler {
} }
// CraftBukkit end // CraftBukkit end
if (super.strikeLightning(entity)) { if (super.strikeLightning(entity)) {
this.server.getPlayerList().sendPacketNearby((EntityHuman) null, entity.locX, entity.locY, entity.locZ, 512.0D, dimension, new PacketPlayOutSpawnEntityWeather(entity)); // CraftBukkit - Use dimension this.server.getPlayerList().sendPacketNearby((EntityHuman) null, entity.locX, entity.locY, entity.locZ, 512.0D, this, new PacketPlayOutSpawnEntityWeather(entity)); // CraftBukkit - Use dimension, // Paper - use world instead of dimension
return true; return true;
} else { } else {
return false; return false;
@@ -1369,8 +1369,8 @@ public class WorldServer extends World implements IAsyncTaskHandler {
BlockActionData blockactiondata = (BlockActionData) iterator.next(); BlockActionData blockactiondata = (BlockActionData) iterator.next();
if (this.a(blockactiondata)) { if (this.a(blockactiondata)) {
// CraftBukkit - this.worldProvider.dimension -> this.dimension // CraftBukkit - this.worldProvider.dimension -> this.dimension, // Paper - dimension -> world
this.server.getPlayerList().sendPacketNearby((EntityHuman) null, blockactiondata.a().getX(), blockactiondata.a().getY(), blockactiondata.a().getZ(), 64.0D, dimension, new PacketPlayOutBlockAction(blockactiondata.a(), blockactiondata.d(), blockactiondata.b(), blockactiondata.c())); this.server.getPlayerList().sendPacketNearby((EntityHuman) null, (double) blockactiondata.a().getX(), (double) blockactiondata.a().getY(), (double) blockactiondata.a().getZ(), 64.0D, this, new PacketPlayOutBlockAction(blockactiondata.a(), blockactiondata.d(), blockactiondata.b(), blockactiondata.c()));
} }
} }

View File

@@ -1922,17 +1922,20 @@ public final class CraftServer implements Server {
@Override @Override
public void reloadPermissions() { public void reloadPermissions() {
((SimplePluginManager) pluginManager).clearPermissions(); pluginManager.clearPermissions();
loadCustomPermissions(); if (com.destroystokyo.paper.PaperConfig.loadPermsBeforePlugins) loadCustomPermissions();
for (Plugin plugin : pluginManager.getPlugins()) { for (Plugin plugin : pluginManager.getPlugins()) {
plugin.getDescription().getPermissions().forEach((perm) -> { for (Permission perm : plugin.getDescription().getPermissions()) {
try { try {
pluginManager.addPermission(perm); pluginManager.addPermission(perm);
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
getLogger().log(Level.WARNING, "Plugin " + plugin.getDescription().getFullName() + " tried to register permission '" + perm.getName() + "' but it's already registered", 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 @Override