Fixed tickloop + minehq optimizations

This commit is contained in:
Beaness 2022-08-01 12:56:36 +02:00
parent 23e68a05f9
commit 1d1ba70ae9
32 changed files with 819 additions and 134 deletions

View File

@ -15,6 +15,7 @@ repositories {
maven(url = "https://oss.sonatype.org/content/groups/public")
maven(url = "https://hub.spigotmc.org/nexus/content/groups/public")
maven(url = "https://repo.velocitypowered.com/snapshots/")
maven(url = "https://repo.viaversion.com")
}
val minecraftVersion = "1_8_R3"

View File

@ -101,6 +101,18 @@ public class eSpigotConfig
fixSprintEatExploit = getBoolean( "settings.fix-sprint-eat-exploit", true );
}
public static boolean reduceArmorDamage;
private static void reduceArmorDamage()
{
reduceArmorDamage = getBoolean( "settings.reduce-armor-damage", false );
}
public static boolean slowerSaturationLoss;
private static void slowerSaturationLoss()
{
slowerSaturationLoss = getBoolean( "settings.slower-saturation-loss", true );
}
public static boolean fixBouncingArrows;
private static void fixBouncingArrows()
{

View File

@ -16,26 +16,6 @@ public class MinecraftPipeline extends ChannelInitializer<SocketChannel>
}
protected void initChannel(SocketChannel channel) {
ChannelConfig config = channel.config();
try {
config.setOption(ChannelOption.SO_KEEPALIVE, true);
} catch (Exception ignored) {}
try {
config.setOption(ChannelOption.TCP_NODELAY, true);
} catch (Exception ignored) {}
try {
config.setOption(ChannelOption.TCP_FASTOPEN, 1);
} catch (Exception ignored) {}
try {
config.setOption(ChannelOption.TCP_FASTOPEN_CONNECT, true);
} catch (Exception ignored) {}
try {
config.setOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
} catch (Exception ignored) {}
try {
config.setOption(ChannelOption.IP_TOS, 0x18);
} catch (Exception ignored) {}
if(PaperSpigotConfig.nettyReadTimeout) channel.pipeline().addLast("timeout", new ReadTimeoutHandler(30));
channel.pipeline()

View File

@ -0,0 +1,141 @@
package com.elevatemc.spigot.tickloop;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Queue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.LockSupport;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
public abstract class IAsyncHandler<R extends Runnable> implements Executor {
private static final Logger LOGGER = LogManager.getLogger();
private final String name;
private final Queue<R> pendingRunnables = new ConcurrentLinkedQueue<>();
private int terminateCount;
protected IAsyncHandler(String name) {
this.name = name;
}
protected abstract R packUpRunnable(Runnable runnable);
protected abstract boolean shouldRun(R task);
public boolean isMainThread() {
return Thread.currentThread() == this.getMainThread();
}
protected abstract Thread getMainThread();
protected boolean executables() {
return !this.isMainThread();
}
public int getPendingRunnables() {
return this.pendingRunnables.size();
}
public String getName() {
return this.name;
}
public <V> CompletableFuture<V> submit(Supplier<V> task) {
return this.executables() ? CompletableFuture.supplyAsync(task, this)
: CompletableFuture.completedFuture(task.get());
}
private CompletableFuture<Void> submitAsync(Runnable runnable) {
return CompletableFuture.supplyAsync(() -> {
runnable.run();
return null;
}, this);
}
public CompletableFuture<Void> submit(Runnable task) {
if (this.executables()) {
return this.submitAsync(task);
} else {
task.run();
return CompletableFuture.completedFuture(null);
}
}
public void performBlocking(Runnable runnable) {
if (!this.isMainThread()) {
this.submitAsync(runnable).join();
} else {
runnable.run();
}
}
public void call(R runnable) {
this.pendingRunnables.add(runnable);
LockSupport.unpark(this.getMainThread());
}
@Override
public void execute(Runnable runnable) {
if (this.executables()) {
this.call(this.packUpRunnable(runnable));
} else {
runnable.run();
}
}
protected void clearAllRunnable() {
this.pendingRunnables.clear();
}
public void runAllRunnable() {
while (this.drawRunnable()) {
}
}
public boolean drawRunnable() {
R runnable = this.pendingRunnables.peek();
if (runnable == null) {
return false;
} else if (this.terminateCount == 0 && !this.shouldRun(runnable)) {
return false;
} else {
this.doRunnable(this.pendingRunnables.remove());
return true;
}
}
public void controlTerminate(BooleanSupplier stopCondition) {
++this.terminateCount;
try {
while (!stopCondition.getAsBoolean()) {
if (!this.drawRunnable()) {
this.waitForRuns();
}
}
} finally {
--this.terminateCount;
}
}
protected void waitForRuns() {
Thread.yield();
LockSupport.parkNanos("waiting for tasks", 100000L);
}
protected void doRunnable(R task) {
try {
task.run();
} catch (Exception e) {
if (e.getCause() instanceof ThreadDeath) throw e;
LOGGER.fatal("Error executing task on {}", this.getName(), e);
}
}
}

View File

@ -0,0 +1,29 @@
package com.elevatemc.spigot.tickloop;
public abstract class ReentrantIAsyncHandler<R extends Runnable> extends IAsyncHandler<R> {
private int count;
public ReentrantIAsyncHandler(String name) {
super(name);
}
@Override
protected boolean executables() {
return this.runningTask() || super.executables();
}
protected boolean runningTask() {
return this.count != 0;
}
@Override
protected void doRunnable(R task) {
++this.count;
try {
super.doRunnable(task);
} finally {
--this.count;
}
}
}

View File

@ -0,0 +1,20 @@
package com.elevatemc.spigot.tickloop;
public class TasksPerTick implements Runnable {
private final int tick;
private final Runnable task;
public TasksPerTick(int creationTicks, Runnable task) {
this.tick = creationTicks;
this.task = task;
}
public int getTick() {
return tick;
}
@Override
public void run() {
task.run();
}
}

View File

@ -0,0 +1,225 @@
package com.elevatemc.spigot.util;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import net.jafama.FastMath;
import net.minecraft.server.EntityHuman;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.MathHelper;
import net.minecraft.server.Packet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
public class PlayerMap {
private static final int CHUNK_BITS = 5;
private static long xzToKey(long x, long z) {
return (x << 32) + z - Integer.MIN_VALUE;
}
private final Long2ObjectOpenHashMap<List<EntityPlayer>> map = new Long2ObjectOpenHashMap<>();
public void add(EntityPlayer player) {
int x = MathHelper.floor(player.locX) >> CHUNK_BITS;
int z = MathHelper.floor(player.locZ) >> CHUNK_BITS;
long key = xzToKey(x, z);
List<EntityPlayer> list = map.get(key);
if (list == null) {
list = new ArrayList<>();
map.put(key, list);
}
list.add(player);
player.playerMapX = x;
player.playerMapZ = z;
}
public void move(EntityPlayer player) {
int x = MathHelper.floor(player.locX) >> CHUNK_BITS;
int z = MathHelper.floor(player.locZ) >> CHUNK_BITS;
// did we move?
if (x == player.playerMapX && z == player.playerMapZ) {
return;
}
// do remove
long key = xzToKey(player.playerMapX, player.playerMapZ);
List<EntityPlayer> list = map.get(key);
list.remove(player);
if (list.isEmpty()) {
map.remove(key);
}
// do add
key = xzToKey(x, z);
list = map.get(key);
if (list == null) {
list = new ArrayList<EntityPlayer>();
map.put(key, list);
}
list.add(player);
player.playerMapX = x;
player.playerMapZ = z;
}
public void remove(EntityPlayer player) {
long key = xzToKey(player.playerMapX, player.playerMapZ);
List<EntityPlayer> list = map.get(key);
if (list == null) {
// player has not yet been added to this playermap, this happens when teleporting to another world during PlayerJoinEvent
return;
}
list.remove(player);
if (list.isEmpty()) {
map.remove(key);
}
}
public EntityPlayer getNearestPlayer(double x, double y, double z, double distance) {
double bestDistanceSqrd = -1.0;
EntityPlayer bestPlayer = null;
for (int chunkX = MathHelper.floor(x - distance) >> CHUNK_BITS; chunkX <= MathHelper.floor(x + distance) >> CHUNK_BITS; chunkX++) {
for (int chunkZ = MathHelper.floor(z - distance) >> CHUNK_BITS; chunkZ <= MathHelper.floor(z + distance) >> CHUNK_BITS; chunkZ++) {
List<EntityPlayer> players = map.get(xzToKey(chunkX, chunkZ));
if (players != null) {
for (EntityPlayer player : players) {
double playerDistSqrd = player.e(x, y, z);
if (playerDistSqrd < distance * distance && (bestDistanceSqrd == -1.0 || playerDistSqrd < bestDistanceSqrd)) {
bestDistanceSqrd = playerDistSqrd;
bestPlayer = player;
}
}
}
}
}
return bestPlayer;
}
public EntityPlayer getNearbyPlayer(double x, double y, double z, double distance, boolean respectSpawningApi) {
double bestDistanceSqrd = -1.0;
EntityPlayer bestPlayer = null;
for (int chunkX = MathHelper.floor(x - distance) >> CHUNK_BITS; chunkX <= MathHelper.floor(x + distance) >> CHUNK_BITS; chunkX++) {
for (int chunkZ = MathHelper.floor(z - distance) >> CHUNK_BITS; chunkZ <= MathHelper.floor(z + distance) >> CHUNK_BITS; chunkZ++) {
List<EntityPlayer> players = map.get(xzToKey(chunkX, chunkZ));
if (players != null) {
for (EntityPlayer player : players) {
if (player != null && !player.dead && (!respectSpawningApi || player.affectsSpawning)) {
double playerDistSqrd = player.e(x, y, z);
if (playerDistSqrd < distance * distance && (bestDistanceSqrd == -1.0 || playerDistSqrd < bestDistanceSqrd)) {
bestDistanceSqrd = playerDistSqrd;
bestPlayer = player;
}
}
}
}
}
}
return bestPlayer;
}
public List<EntityPlayer> getNearbyPlayersIgnoreHeight(double x, double y, double z, double distance) {
List<EntityPlayer> toReturn = null;
for (int chunkX = MathHelper.floor(x - distance) >> CHUNK_BITS; chunkX <= MathHelper.floor(x + distance) >> CHUNK_BITS; chunkX++) {
for (int chunkZ = MathHelper.floor(z - distance) >> CHUNK_BITS; chunkZ <= MathHelper.floor(z + distance) >> CHUNK_BITS; chunkZ++) {
List<EntityPlayer> players = map.get(xzToKey(chunkX, chunkZ));
if (players != null) {
for (EntityPlayer player : players) {
if (player != null && !player.dead && FastMath.abs(player.locX - x) <= distance && FastMath.abs(player.locZ - z) <= distance) {
if (toReturn == null) {
toReturn = Lists.newArrayList();
}
toReturn.add(player);
}
}
}
}
}
return toReturn == null ? Collections.emptyList() : toReturn;
}
public EntityPlayer getNearestAttackablePlayer(double x, double y, double z, double maxXZ, double maxY, Function<EntityHuman, Double> visibility) {
return getNearestAttackablePlayer(x, y, z, maxXZ, maxY, visibility, null);
}
public EntityPlayer getNearestAttackablePlayer(double x, double y, double z, double maxXZ, double maxY, Function<EntityHuman, Double> visibility, Predicate<EntityHuman> condition) {
double bestDistanceSqrd = -1.0;
EntityPlayer bestPlayer = null;
for (int chunkX = MathHelper.floor(x - maxXZ) >> CHUNK_BITS; chunkX <= MathHelper.floor(x + maxXZ) >> CHUNK_BITS; chunkX++) {
for (int chunkZ = MathHelper.floor(z - maxXZ) >> CHUNK_BITS; chunkZ <= MathHelper.floor(z + maxXZ) >> CHUNK_BITS; chunkZ++) {
List<EntityPlayer> players = map.get(xzToKey(chunkX, chunkZ));
if (players != null) {
for (EntityPlayer player : players) {
if (!player.abilities.isInvulnerable && player.isAlive() && (condition == null || condition.apply(player))) {
double dx = player.locX - x;
double dz = player.locZ - z;
double playerDistSqrd = dx * dx + dz * dz;
double dy = FastMath.abs(player.locY - y);
double distForPlayer = maxXZ;
if (player.isSneaking()) {
distForPlayer *= 0.8;
}
if (visibility != null) {
Double v = visibility.apply(player);
if (v != null) {
distForPlayer *= v;
}
}
// mojang mistake squaring maxY?
if ((maxY < 0.0 || dy < maxY * maxY) && playerDistSqrd < distForPlayer * distForPlayer && (bestDistanceSqrd == -1.0 || playerDistSqrd < bestDistanceSqrd)) {
bestDistanceSqrd = playerDistSqrd;
bestPlayer = player;
}
}
}
}
}
}
return bestPlayer;
}
public void sendPacketNearby(EntityPlayer source, double x, double y, double z, double distance, Packet packet) {
for (int chunkX = MathHelper.floor(x - distance) >> CHUNK_BITS; chunkX <= MathHelper.floor(x + distance) >> CHUNK_BITS; chunkX++) {
for (int chunkZ = MathHelper.floor(z - distance) >> CHUNK_BITS; chunkZ <= MathHelper.floor(z + distance) >> CHUNK_BITS; chunkZ++) {
List<EntityPlayer> players = map.get(xzToKey(chunkX, chunkZ));
if (players != null) {
for (EntityPlayer player : players) {
// don't send self
if (player == source) {
continue;
}
// bukkit visibility api
if (source != null && !player.getBukkitEntity().canSee(source.getBukkitEntity())) {
continue;
}
double playerDistSqrd = player.e(x, y, z);
if (playerDistSqrd < distance * distance) {
player.playerConnection.sendPacket(packet);
}
}
}
}
}
}
}

View File

@ -117,11 +117,11 @@ public abstract class BiomeBase {
this.id = i;
BiomeBase.biomes[i] = this;
this.as = this.a();
this.au.add(new BiomeMeta(EntitySheep.class, 12, 4, 4));
this.au.add(new BiomeMeta(EntitySheep.class, 6, 4, 4)); // MineHQ - less sheep
this.au.add(new BiomeMeta(EntityRabbit.class, 10, 3, 3));
this.au.add(new BiomeMeta(EntityPig.class, 10, 4, 4));
this.au.add(new BiomeMeta(EntityChicken.class, 10, 4, 4));
this.au.add(new BiomeMeta(EntityCow.class, 8, 4, 4));
this.au.add(new BiomeMeta(EntityCow.class, 14, 4, 4)); // MineHQ - more cows
this.at.add(new BiomeMeta(EntitySpider.class, 100, 4, 4));
this.at.add(new BiomeMeta(EntityZombie.class, 100, 4, 4));
this.at.add(new BiomeMeta(EntitySkeleton.class, 100, 4, 4));

View File

@ -1,13 +1,17 @@
package net.minecraft.server;
import com.google.common.collect.Lists;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import org.bukkit.craftbukkit.util.LongHash;
import java.util.List;
public class BiomeCache {
private final WorldChunkManager a;
private long b;
private LongHashMap<BiomeCache.BiomeCacheBlock> c = new LongHashMap();
private Long2ObjectMap<BiomeCacheBlock> c = new Long2ObjectOpenHashMap();
private List<BiomeCache.BiomeCacheBlock> d = Lists.newArrayList();
public BiomeCache(WorldChunkManager worldchunkmanager) {
@ -17,8 +21,10 @@ public class BiomeCache {
public BiomeCache.BiomeCacheBlock a(int i, int j) {
i >>= 4;
j >>= 4;
long k = (long) i & 4294967295L | ((long) j & 4294967295L) << 32;
BiomeCache.BiomeCacheBlock biomecache_biomecacheblock = (BiomeCache.BiomeCacheBlock) this.c.getEntry(k);
// MineHQ start
long k = LongHash.toLong(i, j);
BiomeCacheBlock biomecache_biomecacheblock = (BiomeCacheBlock) this.c.get(k);
// MineHQ end
if (biomecache_biomecacheblock == null) {
biomecache_biomecacheblock = new BiomeCache.BiomeCacheBlock(i, j);
@ -49,7 +55,7 @@ public class BiomeCache {
if (l > 30000L || l < 0L) {
this.d.remove(k--);
long i1 = (long) biomecache_biomecacheblock.c & 4294967295L | ((long) biomecache_biomecacheblock.d & 4294967295L) << 32;
long i1 = LongHash.toLong(biomecache_biomecacheblock.c, biomecache_biomecacheblock.d); // MineHQ
this.c.remove(i1);
}

View File

@ -68,7 +68,14 @@ public class ChunkProviderServer implements IChunkProvider {
// CraftBukkit end
}
public void queueUnload(int i, int j) {
// MineHQ start
public void queueUnload(int x, int z) {
queueUnload(x, z, false);
}
public void queueUnload(int i, int j, boolean checked) {
if (!checked && this.world.getPlayerChunkMap().isChunkInUse(i, j)) return;
// MineHQ end
long key = LongHash.toLong(i, j); // IonSpigot - Only create key once
// PaperSpigot start - Asynchronous lighting updates
Chunk chunk = chunks.get(key); // IonSpigot

View File

@ -37,8 +37,10 @@ public class DedicatedServer extends MinecraftServer implements IMinecraftServer
private boolean s;
// CraftBukkit start - Signature changed
public DedicatedServer(joptsimple.OptionSet options) {
super(options, Proxy.NO_PROXY, DedicatedServer.a);
// PandaSpigot start - Modern tick loop
public DedicatedServer(joptsimple.OptionSet options, Thread serverThread) {
super(options, Proxy.NO_PROXY, DedicatedServer.a, serverThread);
// PandaSpigot end
// CraftBukkit end
Thread thread = new Thread("Server Infinisleeper") {
{

View File

@ -395,6 +395,7 @@ public abstract class EntityHuman extends EntityLiving {
double d0 = this.locX;
double d1 = this.locY;
double d2 = this.locZ;
World world = this.world;
float f = this.yaw;
float f1 = this.pitch;
@ -1413,7 +1414,10 @@ public abstract class EntityHuman extends EntityLiving {
super.g(f, f1);
}
this.checkMovement(this.locX - d0, this.locY - d1, this.locZ - d2);
// Kohi - don't check if world changed
if (this.world == world) {
this.checkMovement(this.locX - d0, this.locY - d1, this.locZ - d2);
}
}
public float bI() {

View File

@ -1740,6 +1740,7 @@ public abstract class EntityLiving extends Entity {
break;
} // Spigot
Entity entity = (Entity) o;
if (entity instanceof EntityPlayer) continue; // MineHQ - players don't get pushed
// TODO better check now?
// CraftBukkit start - Only handle mob (non-player) collisions every other tick

View File

@ -91,6 +91,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
// Spigot end
// MineHQ start
public int playerMapX;
public int playerMapZ;
// MineHQ end
public EntityPlayer(MinecraftServer minecraftserver, WorldServer worldserver, GameProfile gameprofile, PlayerInteractManager playerinteractmanager) {
super(worldserver, gameprofile);
this.viewDistance = world.spigotConfig.viewDistance; // PaperSpigot - Player view distance API
@ -671,6 +675,15 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
if (this.vehicle != entity1) { // CraftBukkit
this.playerConnection.sendPacket(new PacketPlayOutAttachEntity(0, this, this.vehicle));
this.playerConnection.a(this.locX, this.locY, this.locZ, this.yaw, this.pitch);
// MineHQ start
if (this.vehicle instanceof EntityLiving) {
AttributeMapServer attributemapserver = (AttributeMapServer) ((EntityLiving) this.vehicle).getAttributeMap();
Collection collection = attributemapserver.c();
if (!collection.isEmpty()) {
this.playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(this.vehicle.getId(), collection));
}
}
// MineHQ end
}
}

View File

@ -261,7 +261,11 @@ public class EntityTrackerEntry {
}
if (!cancelled) {
this.broadcastIncludingSelf(new PacketPlayOutEntityVelocity(this.tracker));
if (this.tracker instanceof EntityPlayer) {
((EntityPlayer) this.tracker).playerConnection.sendPacket(new PacketPlayOutEntityVelocity(this.tracker));
} else if (this.tracker instanceof EntityArrow || this.tracker instanceof EntityProjectile) {
this.broadcast(new PacketPlayOutEntityVelocity(this.tracker));
}
}
// CraftBukkit end
this.tracker.velocityChanged = false;
@ -303,15 +307,21 @@ public class EntityTrackerEntry {
if (this.tracker instanceof EntityLiving) {
AttributeMapServer attributemapserver = (AttributeMapServer) ((EntityLiving) this.tracker).getAttributeMap();
Set set = attributemapserver.getAttributes();
Set<AttributeInstance> set = attributemapserver.getAttributes();
if (!set.isEmpty()) {
// CraftBukkit start - Send scaled max health
if (this.tracker instanceof EntityPlayer) {
((EntityPlayer) this.tracker).getBukkitEntity().injectScaledMaxHealth(set, false);
((EntityPlayer) this.tracker).playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(this.tracker.getId(), set)); // MineHQ
}
// CraftBukkit end
this.broadcastIncludingSelf(new PacketPlayOutUpdateAttributes(this.tracker.getId(), set));
// MineHQ start
// this.broadcastIncludingSelf(new PacketPlayOutUpdateAttributes(this.tracker.getId(), set)); // CraftBukkit
if (this.tracker.passenger instanceof EntityPlayer) {
((EntityPlayer) this.tracker.passenger).playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(this.tracker.getId(), set));
}
// MineHQ end
}
set.clear();
@ -375,6 +385,8 @@ public class EntityTrackerEntry {
entityplayer.playerConnection.sendPacket(new PacketPlayOutUpdateEntityNBT(this.tracker.getId(), nbttagcompound));
}
// MineHQ start
/*
if (this.tracker instanceof EntityLiving) {
AttributeMapServer attributemapserver = (AttributeMapServer) ((EntityLiving) this.tracker).getAttributeMap();
Collection collection = attributemapserver.c();
@ -389,6 +401,8 @@ public class EntityTrackerEntry {
entityplayer.playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(this.tracker.getId(), collection));
}
}
*/
// MineHQ end
this.j = this.tracker.motX;
this.k = this.tracker.motY;

View File

@ -1,5 +1,7 @@
package net.minecraft.server;
import com.elevatemc.spigot.config.eSpigotConfig;
public class FoodMetaData {
public int foodLevel = 20;
@ -44,7 +46,7 @@ public class FoodMetaData {
if (this.exhaustionLevel > 4.0F) {
this.exhaustionLevel -= 4.0F;
if (this.saturationLevel > 0.0F) {
this.saturationLevel = Math.max(this.saturationLevel - 1.0F, 0.0F);
this.saturationLevel = Math.max(this.saturationLevel - (eSpigotConfig.slowerSaturationLoss ? 0.5F : 1.0F), 0.0F); // ElevateMC slower saturation loss
} else if (enumdifficulty != EnumDifficulty.PEACEFUL) {
// CraftBukkit start
org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(entityhuman, Math.max(this.foodLevel - 1, 0));

View File

@ -2,6 +2,8 @@ package net.minecraft.server;
import co.aikar.timings.SpigotTimings;
import com.elevatemc.spigot.threading.ThreadingManager;
import com.elevatemc.spigot.tickloop.ReentrantIAsyncHandler;
import com.elevatemc.spigot.tickloop.TasksPerTick;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Futures;
@ -43,7 +45,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
// CraftBukkit end
public abstract class MinecraftServer implements Runnable, ICommandListener, IAsyncTaskHandler, IMojangStatistics {
public abstract class MinecraftServer extends ReentrantIAsyncHandler<TasksPerTick> implements ICommandListener, IAsyncTaskHandler, IMojangStatistics { // PandaSpigot - Modern tick loop
public static final Logger LOGGER = LogManager.getLogger();
public static final File a = new File("usercache.json");
@ -176,8 +178,38 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
private final ThreadingManager threadingManager;
public MinecraftServer(OptionSet options, Proxy proxy, File file1) {
// PandaSpigot start - Modern tick loop
private long nextTickTime;
private long delayedTasksMaxNextTickTime;
private boolean mayHaveDelayedTasks;
private boolean forceTicks;
private volatile boolean isReady;
private long lastOverloadWarning;
public long serverStartTime;
public volatile Thread shutdownThread;
private long lastTick = 0;
private long catchupTime = 0;
public static <S extends MinecraftServer> S spin(java.util.function.Function<Thread, S> serverFactory) {
java.util.concurrent.atomic.AtomicReference<S> reference = new java.util.concurrent.atomic.AtomicReference<>();
Thread thread = new Thread(() -> reference.get().run(), "Server thread");
thread.setUncaughtExceptionHandler((thread1, throwable) -> MinecraftServer.LOGGER.error(throwable));
S server = serverFactory.apply(thread);
reference.set(server);
thread.setPriority(Thread.NORM_PRIORITY + 2);
thread.start();
return server;
}
public MinecraftServer(OptionSet options, Proxy proxy, File file1, Thread thread) {
super("Server");
io.netty.util.ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED); // [Nacho-0040] Change deprecated Netty parameter // Spigot - disable
this.nextTickTime = getMillis();
this.primaryThread = thread;
this.serverThread = thread;
// PandaSpigot end
this.e = proxy;
this.threadingManager = new ThreadingManager();
MinecraftServer.l = this;
@ -216,8 +248,8 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
*/ // PandaSpigot
Runtime.getRuntime().addShutdownHook(new org.bukkit.craftbukkit.util.ServerShutdownThread(this));
this.serverThread = primaryThread = new Thread(this, "Server thread"); // Moved from main
serverThread.setPriority(10); // Set main thread priority to highest (10)
//this.serverThread = primaryThread = new Thread(this, "Server thread"); // Moved from main // PandaSpigot - comment out; we assign above
// serverThread.setPriority(10); // Set main thread priority to highest (10)
}
public static void main(final OptionSet options) { // CraftBukkit - replaces main(String[] astring)
@ -304,6 +336,7 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
});
*/
/* // PandaSpigot start - comment out
DedicatedServer dedicatedserver = new DedicatedServer(options);
if (options.has("port")) {
@ -323,6 +356,7 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
dedicatedserver.primaryThread.start();
// CraftBukkit end
*/ // PandaSpigot end
} catch (Exception exception) {
MinecraftServer.LOGGER.fatal("Failed to start the minecraft server", exception);
}
@ -671,10 +705,18 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
this.isRunning = false;
}
@Override
// PandaSpigot start - Modern tick loop
public static long getMillis() {
return getNanos() / 1000000L;
}
public static long getNanos() {
return System.nanoTime();
}
// PandaSpigot end
public void run() {
AffinityLock affinityLock = null;
try (final AffinityLock lock = AffinityLock.acquireLock()) {
this.serverStartTime = getNanos();
if (this.init()) {
// eSpigot start - Thread affinity
MinecraftServer.LOGGER.info("[eSpigot] Locked main thread. CPU: " + lock.cpuId());
@ -683,7 +725,7 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
// eSpigot end
this.ab = az();
long i = 0L;
//long i = 0L; // PandaSpigot - comment out; not used
this.r.setMOTD(new ChatComponentText(this.motd));
this.r.setServerInfo(new ServerPing.ServerData("1.8.8", 47));
@ -692,36 +734,21 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
// Spigot start
// PaperSpigot start - Further improve tick loop
Arrays.fill(recentTps, 20);
long start = System.nanoTime(), lastTick = start - TICK_TIME, nextTick = start + MinecraftServer.TICK_TIME, catchupTime = 0, curTime, wait, tickSection = start;
// PandaSpigot start - Modern tick loop
long start = System.nanoTime(), curTime, tickSection = start;
lastTick = start - TICK_TIME;
// PandaSpigot end
// PaperSpigot end
while (this.isRunning) {
curTime = System.nanoTime();
if (nextTick > curTime) {
Thread.sleep(1L);
continue;
// PandaSpigot start - Modern tick loop
long i = ((curTime = System.nanoTime()) / (1000L * 1000L)) - this.nextTickTime; // Paper
if (i > 5000L && this.nextTickTime - this.lastOverloadWarning >= 30000L) { // CraftBukkit
long j = i / 50L;
if (this.server.getWarnOnOverload()) // CraftBukkit
MinecraftServer.LOGGER.warn("Can't keep up! Is the server overloaded? Running {}ms or {} ticks behind", i, j);
this.nextTickTime += j * 50L;
this.lastOverloadWarning = this.nextTickTime;
}
// PaperSpigot start - Further improve tick loop
wait = MinecraftServer.TICK_TIME - (curTime - lastTick);
if (wait > 0L) {
if (catchupTime < 2E6) {
wait += Math.abs(catchupTime);
} else /* KigPaper */ if (wait < catchupTime) {
catchupTime -= wait;
wait = 0L;
} else /*if (catchupTime > 2E6)*/ { // KigPaper
wait -= catchupTime;
catchupTime = 0L;
}
}
if (wait > 0L) {
Thread.sleep(1L);
curTime = System.nanoTime(); // KigPaper
wait = MinecraftServer.TICK_TIME - (curTime - lastTick);
}
nextTick = curTime + (MinecraftServer.TICK_TIME - catchupTime);
catchupTime = Math.min(MinecraftServer.MAX_CATCHUP_BUFFER, catchupTime - wait);
if (++MinecraftServer.currentTick % MinecraftServer.SAMPLE_INTERVAL == 0) {
long diff = curTime - tickSection;
double currentTps = 1E9 / diff * MinecraftServer.SAMPLE_INTERVAL;
@ -738,13 +765,16 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
}
lastTick = curTime;
long start2 = System.nanoTime();
this.A();
this.Q = true;
lastTickTime = System.nanoTime() - start2;
this.nextTickTime += 50L;
this.methodProfiler.a("tick");
this.A(this::haveTime);
this.methodProfiler.c("nextTickWait");
this.mayHaveDelayedTasks = true;
this.delayedTasksMaxNextTickTime = Math.max(getMillis() + 50L, this.nextTickTime);
this.waitUntilNextTick();
this.methodProfiler.b();
this.isReady = true;
// PandaSpigot end
}
// Spigot end
} else {
@ -802,6 +832,62 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
}
// PandaSpigot start - Modern tick loop
private boolean haveTime() {
if (isOversleep) return canOversleep();
return this.forceTicks || this.runningTask() || getMillis() < (this.mayHaveDelayedTasks ? this.delayedTasksMaxNextTickTime : this.nextTickTime);
}
boolean isOversleep = false;
private boolean canOversleep() {
return this.mayHaveDelayedTasks && getMillis() < this.delayedTasksMaxNextTickTime;
}
private boolean canSleepForTickNoOversleep() {
return this.forceTicks || this.runningTask() || getMillis() < this.nextTickTime;
}
private void executeModerately() {
this.runAllRunnable();
java.util.concurrent.locks.LockSupport.parkNanos("executing tasks", 1000L);
}
protected void waitUntilNextTick() {
this.controlTerminate(() -> !this.canSleepForTickNoOversleep());
}
@Override
protected TasksPerTick packUpRunnable(Runnable runnable) {
// anything that does try to post to main during watchdog crash, run on watchdog
if (this.hasStopped && Thread.currentThread().equals(shutdownThread)) {
runnable.run();
runnable = () -> {};
}
return new TasksPerTick(this.ticks, runnable);
}
@Override
protected boolean shouldRun(TasksPerTick task) {
return task.getTick() + 3 < this.ticks || this.haveTime();
}
@Override
public boolean drawRunnable() {
boolean flag = this.pollTaskInternal();
this.mayHaveDelayedTasks = flag;
return flag;
}
private boolean pollTaskInternal() {
return super.drawRunnable();
}
@Override
public Thread getMainThread() {
return serverThread;
}
// PandaSpigot end
private void a(ServerPing serverping) {
File file = this.d("server-icon.png");
@ -841,9 +927,14 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
protected void z() {
}
protected void A() throws ExceptionWorldConflict { // CraftBukkit - added throws
// PandaSpigot start - Modern tick loop
protected void A(java.util.function.BooleanSupplier shouldKeepTicking) throws ExceptionWorldConflict { // CraftBukkit - added throws
co.aikar.timings.TimingsManager.FULL_SERVER_TICK.startTiming(); // Spigot
long i = System.nanoTime();
isOversleep = true;
this.controlTerminate(() -> !this.canOversleep());
isOversleep = false;
// PandaSpigot end
++this.ticks;
if (this.T) {
@ -886,6 +977,10 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
ThreadingManager.checkTickTime(tickTime);
// Migot end
// PandaSpigot start - Modern tick loop
long endTime = System.nanoTime();
long remaining = (TICK_TIME - (endTime - lastTick)) - catchupTime;
// PandaSpigot end
this.methodProfiler.a("tallying");
this.h[this.ticks % 100] = System.nanoTime() - i;
this.methodProfiler.b();

View File

@ -21,6 +21,8 @@ public class PathfinderGoalRandomStroll extends PathfinderGoal {
}
public boolean a() {
// MineHQ start - disable RandomStroll AI
/*
if (!this.g) {
if (this.a.bh() >= 100) {
return false;
@ -41,6 +43,9 @@ public class PathfinderGoalRandomStroll extends PathfinderGoal {
this.g = false;
return true;
}
*/
return false;
// MineHQ end
}
public boolean b() {

View File

@ -21,8 +21,8 @@ public class PlayerChunkMap {
private final WorldServer world;
private final List<EntityPlayer> managedPlayers = Lists.newArrayList();
private final LongHashMap<PlayerChunkMap.PlayerChunk> d = new LongHashMap();
private final Queue<PlayerChunkMap.PlayerChunk> e = new java.util.concurrent.ConcurrentLinkedQueue<>(); // CraftBukkit ArrayList -> ConcurrentLinkedQueue
private final Queue<PlayerChunkMap.PlayerChunk> f = new java.util.concurrent.ConcurrentLinkedQueue<>(); // CraftBukkit ArrayList -> ConcurrentLinkedQueue
private final List<PlayerChunk> e = new ArrayList<>(); // Kohi - use arraylist as in vanilla
// private final Queue f = new java.util.concurrent.ConcurrentLinkedQueue(); // Kohi - this is pointless
private int g;
private long h;
private final int[][] i = new int[][] { { 1, 0}, { 0, 1}, { -1, 0}, { 0, -1}};
@ -40,8 +40,8 @@ public class PlayerChunkMap {
public void flush() {
long i = this.world.getTime();
int j;
PlayerChunkMap.PlayerChunk playerchunkmap_playerchunk;
/* Kohi - removed PlayerChunkMap.f
if (i - this.h > 8000L) {
this.h = i;
@ -59,9 +59,16 @@ public class PlayerChunkMap {
iterator.remove();
// CraftBukkit end
}
}
} */
// this.e.clear(); //CraftBukkit - Removals are already covered
for (PlayerChunk playerchunk : this.e) {
playerchunk.b();
}
this.e.clear();
// MineHQ start - chunk GC handles this
/*
if (this.managedPlayers.isEmpty()) {
if (!wasNotEmpty) return; // CraftBukkit - Only do unload when we go from non-empty to empty
WorldProvider worldprovider = this.world.worldProvider;
@ -75,6 +82,8 @@ public class PlayerChunkMap {
wasNotEmpty = true;
}
// CraftBukkit end
*/
// MineHQ end
}
@ -91,7 +100,7 @@ public class PlayerChunkMap {
if (playerchunkmap_playerchunk == null && flag) {
playerchunkmap_playerchunk = new PlayerChunkMap.PlayerChunk(i, j);
this.d.put(k, playerchunkmap_playerchunk);
this.f.add(playerchunkmap_playerchunk);
// this.f.add(playerchunkmap_playerchunk);
}
return playerchunkmap_playerchunk;
@ -108,6 +117,7 @@ public class PlayerChunkMap {
// CraftBukkit end
public void flagDirty(BlockPosition blockposition) {
org.spigotmc.AsyncCatcher.catchOp("PlayerChunkMap.flagDirty");
int i = blockposition.getX() >> 4;
int j = blockposition.getZ() >> 4;
PlayerChunkMap.PlayerChunk playerchunkmap_playerchunk = this.a(i, j, false);
@ -119,8 +129,10 @@ public class PlayerChunkMap {
}
public void addPlayer(EntityPlayer entityplayer) {
int i = (int) entityplayer.locX >> 4;
int j = (int) entityplayer.locZ >> 4;
// Poweruser start
int i = MathHelper.floor(entityplayer.locX) >> 4;
int j = MathHelper.floor(entityplayer.locZ) >> 4;
// Poweruser end
entityplayer.d = entityplayer.locX;
entityplayer.e = entityplayer.locZ;
@ -150,8 +162,10 @@ public class PlayerChunkMap {
ArrayList arraylist = Lists.newArrayList(entityplayer.chunkCoordIntPairQueue);
int i = 0;
int j = entityplayer.viewDistance; // PaperSpigot - Player view distance API
int k = (int) entityplayer.locX >> 4;
int l = (int) entityplayer.locZ >> 4;
// Poweruser start
int k = MathHelper.floor(entityplayer.locX) >> 4;
int l = MathHelper.floor(entityplayer.locZ) >> 4;
// Poweruser end
int i1 = 0;
int j1 = 0;
ChunkCoordIntPair chunkcoordintpair = this.a(k, l, true).location;
@ -192,8 +206,10 @@ public class PlayerChunkMap {
}
public void removePlayer(EntityPlayer entityplayer) {
int i = (int) entityplayer.d >> 4;
int j = (int) entityplayer.e >> 4;
// Poweruser start
int i = MathHelper.floor(entityplayer.d) >> 4;
int j = MathHelper.floor(entityplayer.e) >> 4;
// Poweruser end
// PaperSpigot start - Player view distance API
for (int k = i - entityplayer.viewDistance; k <= i + entityplayer.viewDistance; ++k) {
@ -218,15 +234,19 @@ public class PlayerChunkMap {
}
public void movePlayer(EntityPlayer entityplayer) {
int i = (int) entityplayer.locX >> 4;
int j = (int) entityplayer.locZ >> 4;
// Poweruser start
int i = MathHelper.floor(entityplayer.locX) >> 4;
int j = MathHelper.floor(entityplayer.locZ) >> 4;
// Poweruser end
double d0 = entityplayer.d - entityplayer.locX;
double d1 = entityplayer.e - entityplayer.locZ;
double d2 = d0 * d0 + d1 * d1;
if (d2 >= 64.0D) {
int k = (int) entityplayer.d >> 4;
int l = (int) entityplayer.e >> 4;
// Poweruser start
int k = MathHelper.floor(entityplayer.d) >> 4;
int l = MathHelper.floor(entityplayer.e) >> 4;
// Poweruser end
int i1 = entityplayer.viewDistance; // PaperSpigot - Player view distance API
int j1 = i - k;
int k1 = j - l;
@ -277,12 +297,13 @@ public class PlayerChunkMap {
i = MathHelper.clamp(i, 3, 32);
if (i != this.g) {
int j = i - this.g;
ArrayList arraylist = Lists.newArrayList(this.managedPlayers);
List<EntityPlayer> arraylist = Lists.newArrayList(this.managedPlayers);
for (Object o : arraylist) {
EntityPlayer entityplayer = (EntityPlayer) o;
int k = (int) entityplayer.locX >> 4;
int l = (int) entityplayer.locZ >> 4;
for (EntityPlayer entityplayer : arraylist) {
// Poweruser start
int k = MathHelper.floor(entityplayer.locX) >> 4;
int l = MathHelper.floor(entityplayer.locZ) >> 4;
// Poweruser end
int i1;
int j1;
@ -315,8 +336,8 @@ public class PlayerChunkMap {
public void updateViewDistance(EntityPlayer player, int viewDistance) {
viewDistance = MathHelper.clamp(viewDistance, 3, 32);
if (viewDistance != player.viewDistance) {
int cx = (int) player.locX >> 4;
int cz = (int) player.locZ >> 4;
int cx = MathHelper.floor(player.locX) >> 4;
int cz = MathHelper.floor(player.locZ) >> 4;
if (viewDistance - player.viewDistance > 0) {
for (int x = cx - viewDistance; x <= cx + viewDistance; ++x) {
@ -415,7 +436,7 @@ public class PlayerChunkMap {
ChunkIOExecutor.dropQueuedChunkLoad(PlayerChunkMap.this.a(), this.location.x, this.location.z, this.loadedRunnable);
long i = (long) this.location.x + 2147483647L | (long) this.location.z + 2147483647L << 32;
PlayerChunkMap.this.d.remove(i);
PlayerChunkMap.this.f.remove(this);
// PlayerChunkMap.this.f.remove(this); Kohi
}
return;
@ -435,7 +456,7 @@ public class PlayerChunkMap {
this.a(chunk);
PlayerChunkMap.this.d.remove(i);
PlayerChunkMap.this.f.remove(this);
// PlayerChunkMap.this.f.remove(this); // Kohi
if (this.dirtyCount > 0) {
PlayerChunkMap.this.e.remove(this);
}
@ -559,8 +580,10 @@ public class PlayerChunkMap {
private int z;
public ChunkCoordComparator (EntityPlayer entityplayer) {
x = (int) entityplayer.locX >> 4;
z = (int) entityplayer.locZ >> 4;
// Poweruser start
x = MathHelper.floor(entityplayer.locX) >> 4;
z = MathHelper.floor(entityplayer.locZ) >> 4;
// Poweruser end
}
public int compare(ChunkCoordIntPair a, ChunkCoordIntPair b) {

View File

@ -463,9 +463,8 @@ public class PlayerConnection implements PacketListenerPlayIn, IUpdatePlayerList
this.player.checkMovement(this.player.locX - d0, this.player.locY - d1, this.player.locZ - d2);
if (!this.player.noclip) {
boolean flag2 = worldserver.getCubes(this.player, this.player.getBoundingBox().shrink(f4, f4, f4)).isEmpty();
boolean rayTraceCollision = delta > 0.3 && worldserver.rayTrace(new Vec3D(this.o, this.p + 1.0, this.q), new Vec3D(d1, d2 + 1.0, d3), false, true, false) != null;
if (flag && (flag1 || !flag2 || rayTraceCollision) && !this.player.isSleeping()) {
if (flag && (flag1 || !flag2) && !this.player.isSleeping()) {
this.a(this.o, this.p, this.q, f2, f3);
return;
}

View File

@ -5,6 +5,7 @@ import java.util.concurrent.Callable;
// CraftBukkit start
import java.util.List;
import com.elevatemc.spigot.config.eSpigotConfig;
import org.bukkit.craftbukkit.entity.CraftHumanEntity;
import org.bukkit.entity.HumanEntity;
// CraftBukkit end
@ -478,7 +479,7 @@ public class PlayerInventory implements IInventory {
}
public void a(float f) {
f /= 4.0F;
f /= eSpigotConfig.reduceArmorDamage ? 8.0F : 4.0F; // MineHQ
if (f < 1.0F) {
f = 1.0F;
}

View File

@ -346,6 +346,7 @@ public abstract class PlayerList {
public void d(EntityPlayer entityplayer) {
entityplayer.u().getPlayerChunkMap().movePlayer(entityplayer);
entityplayer.world.playerMap.move(entityplayer); // MineHQ
}
public String disconnect(EntityPlayer entityplayer) { // CraftBukkit - return string

View File

@ -6,6 +6,7 @@ import com.velocitypowered.natives.util.Natives; // Paper
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollChannelOption;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.kqueue.KQueue;
@ -135,14 +136,20 @@ public class ServerConnection {
LOGGER.info("Using " + Natives.cipher.getLoadedVariant() + " cipher from Velocity.");
// Paper/Nacho end
this.listeningChannels.add(((new ServerBootstrap() // Nacho - deobfuscate listeningChannels
.channel(channel))
ServerBootstrap bootstrap = new ServerBootstrap() // Nacho - deobfuscate listeningChannels
.channel(channel)
.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, SERVER_WRITE_MARK)
.childHandler(new MinecraftPipeline(this))
.childOption(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.IP_TOS, 0x18)
.group(boss, worker)
.localAddress(ip))
.bind()
.syncUninterruptibly());
.localAddress(ip);
if (server.ai()) {
bootstrap.option(ChannelOption.TCP_FASTOPEN, 3);
}
this.listeningChannels.add(bootstrap.bind().syncUninterruptibly());
}
}

View File

@ -2,6 +2,7 @@ package net.minecraft.server;
import com.elevatemc.spigot.config.eSpigotConfig;
import com.elevatemc.spigot.util.OptimizedWorldTileEntitySet;
import com.elevatemc.spigot.util.PlayerMap;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@ -272,6 +273,7 @@ public abstract class World implements IBlockAccess {
return this;
}
public final PlayerMap playerMap = new PlayerMap(); // MineHQ
public BiomeBase getBiome(final BlockPosition blockposition) {
if (this.isLoaded(blockposition)) {
Chunk chunk = this.getChunkAtWorldCoords(blockposition);
@ -1247,6 +1249,7 @@ public abstract class World implements IBlockAccess {
EntityHuman entityhuman = (EntityHuman) entity;
this.players.add(entityhuman);
this.playerMap.add((EntityPlayer) entityhuman); // MineHQ
this.everyoneSleeping();
}
@ -1285,6 +1288,7 @@ public abstract class World implements IBlockAccess {
entity.die();
if (entity instanceof EntityHuman) {
this.players.remove(entity);
this.playerMap.remove((EntityPlayer) entity); // MineHQ
// Spigot start
for ( Object o : worldMaps.c )
{
@ -1314,6 +1318,7 @@ public abstract class World implements IBlockAccess {
entity.die();
if (entity instanceof EntityHuman) {
this.players.remove(entity);
this.playerMap.remove((EntityPlayer) entity); // MineHQ
this.worldMaps.removeTrackedPlayer((EntityHuman) entity); // FlamePaper - Minetick fix memory leaks
this.everyoneSleeping();
}
@ -1408,7 +1413,7 @@ public abstract class World implements IBlockAccess {
{
block = chunk.getBlockData( blockposition );
}
if ( block != null )
if ( block != null && block != Blocks.AIR) // MineHQ
{
// PaperSpigot start - FallingBlocks and TNT collide with specific non-collidable blocks
Block b = block.getBlock();
@ -1510,7 +1515,7 @@ public abstract class World implements IBlockAccess {
} else {
block = chunk.getBlockData(blockposition);
}
if (block != null) {
if (block != null && block != Blocks.AIR) { // MineHQ
// PaperSpigot start - FallingBlocks and TNT collide with specific non-collidable blocks
Block b = block.getBlock();
if (entity.world.paperSpigotConfig.fallingBlocksCollideWithSigns && (entity instanceof EntityTNTPrimed || entity instanceof EntityFallingBlock) && (b instanceof BlockSign || b instanceof BlockFenceGate || b instanceof BlockTorch || b instanceof BlockButtonAbstract || b instanceof BlockLever || b instanceof BlockTripwireHook || b instanceof BlockTripwire || b instanceof BlockChest || b instanceof BlockSlowSand || b instanceof BlockBed || b instanceof BlockEnderChest || b instanceof BlockEnchantmentTable || b instanceof BlockBrewingStand)) {
@ -1575,7 +1580,7 @@ public abstract class World implements IBlockAccess {
iblockdata = Blocks.BEDROCK.getBlockData();
}
iblockdata.getBlock().a(this, blockposition_mutableblockposition, iblockdata, axisalignedbb, arraylist, null);
if (iblockdata != Blocks.AIR.getBlockData()) iblockdata.getBlock().a(this, blockposition_mutableblockposition, iblockdata, axisalignedbb, arraylist, null);
}
}
}
@ -3233,6 +3238,11 @@ public abstract class World implements IBlockAccess {
}
public EntityHuman findNearbyPlayer(double d0, double d1, double d2, double d3) {
// MineHQ start
if (0 <= d3 && d3 <= 64) {
return this.playerMap.getNearestPlayer(d0, d1, d2, d3);
}
// MineHQ end
double d4 = -1.0D;
EntityHuman entityhuman = null;
@ -3278,6 +3288,11 @@ public abstract class World implements IBlockAccess {
}
public EntityHuman findNearbyPlayerWhoAffectsSpawning(double d0, double d1, double d2, double d3) {
// MineHQ start
if (0 <= d3 && d3 <= 64.0) {
return this.playerMap.getNearbyPlayer(d0, d1, d2, d3, true);
}
// MineHQ end
double d4 = -1.0D;
EntityHuman entityhuman = null;

View File

@ -30,13 +30,13 @@ public class WorldManager implements IWorldAccess {
public void a(EntityHuman entityhuman, String s, double d0, double d1, double d2, float f, float f1) {
// CraftBukkit - this.world.dimension
this.a.getPlayerList().sendPacketNearby(entityhuman, d0, d1, d2, f > 1.0F ? (double) (16.0F * f) : 16.0D, this.world.dimension, new PacketPlayOutNamedSoundEffect(s, d0, d1, d2, f, f1));
this.world.playerMap.sendPacketNearby((EntityPlayer) entityhuman, d0, d1, d2, f > 1.0F ? (double) (16.0F * f) : 16.0D, new PacketPlayOutNamedSoundEffect(s, d0, d1, d2, f, f1));
}
// KigPaper start
public void broadcastPacketIncludingSelf(EntityHuman entityhuman, String s, double d0, double d1, double d2, float f, float f1) {
// CraftBukkit - this.world.dimension
this.a.getPlayerList().sendPacketNearbyIncludingSelf(entityhuman, d0, d1, d2, f > 1.0F ? (double) (16.0F * f) : 16.0D, this.world.dimension, new PacketPlayOutNamedSoundEffect(s, d0, d1, d2, f, f1));
this.world.playerMap.sendPacketNearby((EntityPlayer) entityhuman, d0, d1, d2, f > 1.0F ? (double) (16.0F * f) : 16.0D, new PacketPlayOutNamedSoundEffect(s, d0, d1, d2, f, f1));
}
// KigPaper end
@ -52,7 +52,7 @@ public class WorldManager implements IWorldAccess {
public void a(EntityHuman entityhuman, int i, BlockPosition blockposition, int j) {
// CraftBukkit - this.world.dimension
this.a.getPlayerList().sendPacketNearby(entityhuman, blockposition.getX(), blockposition.getY(), blockposition.getZ(), 64.0D, this.world.dimension, new PacketPlayOutWorldEvent(i, blockposition, j, false));
this.world.playerMap.sendPacketNearby((EntityPlayer) entityhuman, blockposition.getX(), blockposition.getY(), blockposition.getZ(), 64.0D, new PacketPlayOutWorldEvent(i, blockposition, j, false));
}
public void a(int i, BlockPosition blockposition, int j) {
@ -60,7 +60,7 @@ public class WorldManager implements IWorldAccess {
}
public void b(int i, BlockPosition blockposition, int j) {
Iterator iterator = this.a.getPlayerList().v().iterator();
/* Iterator iterator = this.world.players.iterator();; // MineHQ
// CraftBukkit start
EntityHuman entityhuman = null;
@ -72,7 +72,7 @@ public class WorldManager implements IWorldAccess {
while (iterator.hasNext()) {
EntityPlayer entityplayer = (EntityPlayer) iterator.next();
if (entityplayer != null && entityplayer.world == this.world && entityplayer.getId() != i) {
if (entityplayer != null && entityplayer.getId() != i) { // MineHQ
double d0 = (double) blockposition.getX() - entityplayer.locX;
double d1 = (double) blockposition.getY() - entityplayer.locY;
double d2 = (double) blockposition.getZ() - entityplayer.locZ;
@ -90,7 +90,11 @@ public class WorldManager implements IWorldAccess {
// SportPaper end
}
}
}
} */
// MineHQ start - PlayerMap
Entity entity = world.a(i);
EntityPlayer player = entity instanceof EntityPlayer ? (EntityPlayer) entity : null;
this.world.playerMap.sendPacketNearby(player, blockposition.getX(), blockposition.getY(), blockposition.getZ(), 32.0D, new PacketPlayOutBlockBreakAnimation(i, blockposition, j));
// MineHQ end
}
}

View File

@ -258,18 +258,18 @@ public class CraftWorld implements World {
public boolean unloadChunkRequest(int x, int z, boolean safe) {
org.spigotmc.AsyncCatcher.catchOp( "chunk unload"); // Spigot
if (safe && isChunkInUse(x, z)) {
if (isChunkInUse(x, z)) { // MineHQ - never unload in-use chunks
return false;
}
world.chunkProviderServer.queueUnload(x, z);
world.chunkProviderServer.queueUnload(x, z, true); // MineHQ
return true;
}
public boolean unloadChunk(int x, int z, boolean save, boolean safe) {
org.spigotmc.AsyncCatcher.catchOp( "chunk unload"); // Spigot
if (safe && isChunkInUse(x, z)) {
if (isChunkInUse(x, z)) { // MineHQ - never unload in-use chunks
return false;
}

View File

@ -9,6 +9,8 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import net.minecraft.server.DedicatedServer;
import net.minecraft.server.DispenserRegistry;
import net.minecraft.server.MinecraftServer;
import org.apache.commons.lang3.JavaVersion;
@ -214,7 +216,30 @@ public class Main {
// Spigot End
System.setProperty("library.jansi.version", "eSpigot"); // PandaSpigot - set meaningless jansi version to prevent git builds from crashing on Windows
System.out.println("Starting the spigot, please wait...");
MinecraftServer.main(options);
// PandaSpigot start - Modern tick loop
DispenserRegistry.c();
OptionSet finalOptions = options;
DedicatedServer server = MinecraftServer.spin(thread -> {
DedicatedServer dedicatedserver = new DedicatedServer(finalOptions, thread);
if (finalOptions.has("port")) {
int port = (Integer) finalOptions.valueOf("port");
if (port > 0) {
dedicatedserver.setPort(port);
}
}
if (finalOptions.has("universe")) {
dedicatedserver.universe = (File) finalOptions.valueOf("universe");
}
if (finalOptions.has("world")) {
dedicatedserver.setWorld((String) finalOptions.valueOf("world"));
}
return dedicatedserver;
});
// PandaSpigot end - Modern tick loop
} catch (Throwable t) {
t.printStackTrace();
}

View File

@ -550,6 +550,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
}
// MineHQ start - don't allow excessive teleports
int locationChunkX = location.getBlockX() >> 4;
int locationChunkZ = location.getBlockZ() >> 4;
if (46340 <= Math.abs(locationChunkX) || 46340 <= Math.abs(locationChunkZ)) {
throw new IllegalArgumentException("Invalid teleportation destination for " + this.getName() + "! Offending location: " + location.toString());
}
// MineHQ end
// From = Players current Location
Location from = this.getLocation();
// To = Players new Location if Teleport is Successful

View File

@ -1,16 +1,16 @@
package org.bukkit.craftbukkit.inventory;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.PacketPlayOutHeldItemSlot;
import net.minecraft.server.PacketPlayOutSetSlot;
import net.minecraft.server.PlayerInventory;
import net.minecraft.server.*;
import org.apache.commons.lang.Validate;
import org.bukkit.Material;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.EntityEquipment;
import org.bukkit.inventory.ItemStack;
import java.util.HashMap;
public class CraftInventoryPlayer extends CraftInventory implements org.bukkit.inventory.PlayerInventory, EntityEquipment {
public CraftInventoryPlayer(net.minecraft.server.PlayerInventory inventory) {
super(inventory);
@ -213,4 +213,40 @@ public class CraftInventoryPlayer extends CraftInventory implements org.bukkit.i
public void setBootsDropChance(float chance) {
throw new UnsupportedOperationException();
}
// MineHQ start
@Override
public HashMap<Integer, ItemStack> addItem(ItemStack... items) {
HashMap<Integer, ItemStack> leftover = super.addItem(items);
this.updatePlayerInventory();
return leftover;
}
@Override
public void remove(Material material) {
super.remove(material);
this.updatePlayerInventory();
}
@Override
public HashMap<Integer, ItemStack> removeItem(ItemStack... items) {
HashMap<Integer, ItemStack> leftover = super.removeItem(items);
this.updatePlayerInventory();
return leftover;
}
@Override
public void remove(ItemStack item) {
super.remove(item);
this.updatePlayerInventory();
}
private void updatePlayerInventory() {
EntityHuman human = this.getInventory().player;
if (human instanceof EntityPlayer) {
EntityPlayer player = (EntityPlayer) human;
player.updateInventory(player.defaultContainer);
}
}
// MineHQ end
}

View File

@ -76,9 +76,11 @@ public class CraftScheduler implements BukkitScheduler {
final ConcurrentHashMap<Integer, CraftTask> runners = new ConcurrentHashMap<Integer, CraftTask>(); // Paper
volatile int currentTick = -1; // Paper
//private final Executor executor = Executors.newCachedThreadPool(new com.google.common.util.concurrent.ThreadFactoryBuilder().setNameFormat("Craft Scheduler Thread - %1$d").build()); // Spigot // Paper - moved to AsyncScheduler
// MineHQ start - nope
private CraftAsyncDebugger debugHead = new CraftAsyncDebugger(-1, null, null) {@Override StringBuilder debugTo(StringBuilder string) {return string;}};
private CraftAsyncDebugger debugTail = debugHead;
private static final int RECENT_TICKS;
// MineHQ end
static {
RECENT_TICKS = 30;
@ -427,7 +429,7 @@ public class CraftScheduler implements BukkitScheduler {
}
parsePending();
} else {
debugTail = debugTail.setNext(new CraftAsyncDebugger(currentTick + RECENT_TICKS, task.getOwner(), task.getTaskClass()));
// debugTail = debugTail.setNext(new CraftAsyncDebugger(currentTick + RECENT_TICKS, task.getOwner(), task.getTaskClass())); // MineHQ - nope
task.getOwner().getLogger().log(Level.SEVERE, "Unexpected Async Task in the Sync Scheduler. Report this to Paper"); // Paper
// We don't need to parse pending
// (async tasks must live with race-conditions if they attempt to cancel between these few lines of code)
@ -442,7 +444,7 @@ public class CraftScheduler implements BukkitScheduler {
}
pending.addAll(temp);
temp.clear();
debugHead = debugHead.getNextHead(currentTick);
// debugHead = debugHead.getNextHead(currentTick); // MineHQ - nope
}
protected void addTask(final CraftTask task) {
@ -505,10 +507,15 @@ public class CraftScheduler implements BukkitScheduler {
@Override
public String toString() {
// MineHQ start
return "";
/*
int debugTick = currentTick;
StringBuilder string = new StringBuilder("Recent tasks from ").append(debugTick - RECENT_TICKS).append('-').append(debugTick).append('{');
debugHead.debugTo(string);
return string.append('}').toString();
*/
// MineHQ end
}
@Deprecated

View File

@ -122,16 +122,16 @@ public class ActivationRange
int k = MathHelper.floor( maxBB.c / 16.0D );
int l = MathHelper.floor( maxBB.f / 16.0D );
Chunk chunk = null; // MineHQ
for ( int i1 = i; i1 <= j; ++i1 )
{
for ( int j1 = k; j1 <= l; ++j1 )
{
Chunk chunk = world.getChunkIfLoaded( i1, j1 ); // SportPaper - remove double chunk lookup
if ( chunk != null )
{
activateChunkEntities( chunk );
// MineHQ start
if ((chunk = world.getChunkIfLoaded(i1, j1)) != null) {
activateChunkEntities(chunk);
}
// MineHQ end
}
}
}

View File

@ -10,6 +10,7 @@
<TimeBasedTriggeringPolicy />
<OnStartupTriggeringPolicy />
</Policies>
<DefaultRolloverStrategy max="999"/>
</RollingRandomAccessFile>
</Appenders>
<Loggers>