SportPaper patches

This commit is contained in:
beaness 2022-06-24 17:54:42 +02:00
parent cb9fadbb9f
commit e48bcb5e7a
81 changed files with 1954 additions and 587 deletions

View File

@ -29,6 +29,7 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.Recipe;
import org.bukkit.map.MapView;
import org.bukkit.permissions.Permissible;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.ServicesManager;
import org.bukkit.plugin.messaging.Messenger;
@ -1162,6 +1163,14 @@ public final class Bukkit {
}
// Paper end
public static void postToMainThread(Plugin plugin, boolean priority, Runnable task) {
server.postToMainThread(plugin, priority, task);
}
public static boolean runOnMainThread(Plugin plugin, boolean priority, Runnable task) {
return server.runOnMainThread(plugin, priority, task);
}
public static Server.Spigot spigot()
{
return server.spigot();

View File

@ -4,6 +4,8 @@ import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Entity;
import java.util.Set;
/**
* Represents a chunk of blocks
*/
@ -40,6 +42,14 @@ public interface Chunk {
*/
Block getBlock(int x, int y, int z);
/**
* Get all blocks in this chunk that are made of the given {@link Material}
*
* @param material type of block to search for
* @return all blocks found
*/
Set<Block> getBlocks(Material material);
/**
* Capture thread-safe read-only snapshot of chunk data
*

View File

@ -29,6 +29,7 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.Recipe;
import org.bukkit.map.MapView;
import org.bukkit.permissions.Permissible;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.ServicesManager;
import org.bukkit.plugin.messaging.Messenger;
@ -950,6 +951,28 @@ public interface Server extends PluginMessageRecipient {
CommandMap getCommandMap();
// Paper end
/**
* Post the given task to the main thread queue. This is the queue used to handle
* incoming packets (NOT the {@link BukkitScheduler} queue).
*
* The priority flag determines which end of the queue the task is posted to, and
* therefor whether it will run before (true) or after (false) any other tasks
* that are currently queued.
*
* Since incoming packets are also handled through this queue,
*/
void postToMainThread(Plugin plugin, boolean priority, Runnable task);
/**
* If called on the main thread, run the given task immediately and return when
* the task completes. If run from any other thread, this is the same as calling
* {@link #postToMainThread(Plugin, boolean, Runnable)}.
*
* @return true if the task ran synchronously,
* false if it was added to the main thread queue
*/
boolean runOnMainThread(Plugin plugin, boolean priority, Runnable task);
class Spigot
{
@Deprecated

View File

@ -32,7 +32,7 @@ public class VersionCommand extends BukkitCommand {
if (args.length == 0) {
String[] message = new String[] {
"§3This server is running §b§leSpigot§3 by the ElevateMC development team. Version §b§l1.8.8",
"§3This server is running §b§leSpigot§3 by the ElevateMC development team. Version §b§l1.8.9",
};
sender.sendMessage(message);

View File

@ -119,6 +119,26 @@ public abstract class MetadataStoreBase<T> {
}
}
/**
* Removes all metadata in the metadata store that originates from the
* given plugin.
*
* @param owningPlugin the plugin requesting the invalidation.
* @throws IllegalArgumentException If plugin is null
*/
public void removeAll(Plugin owningPlugin) {
Validate.notNull(owningPlugin, "Plugin cannot be null");
for (Iterator<Map<Plugin, MetadataValue>> iterator = metadataMap.values().iterator(); iterator.hasNext(); ) {
Map<Plugin, MetadataValue> values = iterator.next();
if (values.containsKey(owningPlugin)) {
values.remove(owningPlugin);
}
if (values.isEmpty()) {
iterator.remove();
}
}
}
/**
* Creates a unique name for the object receiving metadata by combining
* unique data from the subject with a metadataKey.

View File

@ -0,0 +1,36 @@
package org.github.paperspigot.event;
import com.google.common.base.Preconditions;
import org.apache.commons.lang.Validate;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.github.paperspigot.exception.ServerException;
/**
* Called whenever an exception is thrown in a recoverable section of the server.
*/
public class ServerExceptionEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private ServerException exception;
public ServerExceptionEvent (ServerException exception) {
this.exception = Preconditions.checkNotNull(exception, "exception");
}
/**
* Gets the wrapped exception that was thrown.
* @return Exception thrown
*/
public ServerException getException() {
return exception;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}

View File

@ -0,0 +1,64 @@
package org.github.paperspigot.exception;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Thrown when a command throws an exception
*/
public class ServerCommandException extends ServerException {
private final Command command;
private final CommandSender commandSender;
private final String[] arguments;
public ServerCommandException(String message, Throwable cause, Command command, CommandSender commandSender, String[] arguments) {
super(message, cause);
this.commandSender = checkNotNull(commandSender, "commandSender");
this.arguments = checkNotNull(arguments, "arguments");
this.command = checkNotNull(command, "command");
}
public ServerCommandException(Throwable cause, Command command, CommandSender commandSender, String[] arguments) {
super(cause);
this.commandSender = checkNotNull(commandSender, "commandSender");
this.arguments = checkNotNull(arguments, "arguments");
this.command = checkNotNull(command, "command");
}
protected ServerCommandException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Command command, CommandSender commandSender, String[] arguments) {
super(message, cause, enableSuppression, writableStackTrace);
this.commandSender = checkNotNull(commandSender, "commandSender");
this.arguments = checkNotNull(arguments, "arguments");
this.command = checkNotNull(command, "command");
}
/**
* Gets the command which threw the exception
*
* @return exception throwing command
*/
public Command getCommand() {
return command;
}
/**
* Gets the command sender which executed the command request
*
* @return command sender of exception thrown command request
*/
public CommandSender getCommandSender() {
return commandSender;
}
/**
* Gets the arguments which threw the exception for the command
*
* @return arguments of exception thrown command request
*/
public String[] getArguments() {
return arguments;
}
}

View File

@ -0,0 +1,52 @@
package org.github.paperspigot.exception;
import org.bukkit.event.Event;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import static com.google.common.base.Preconditions.*;
/**
* Exception thrown when a server event listener throws an exception
*/
public class ServerEventException extends ServerPluginException {
private final Listener listener;
private final Event event;
public ServerEventException(String message, Throwable cause, Plugin responsiblePlugin, Listener listener, Event event) {
super(message, cause, responsiblePlugin);
this.listener = checkNotNull(listener, "listener");
this.event = checkNotNull(event, "event");
}
public ServerEventException(Throwable cause, Plugin responsiblePlugin, Listener listener, Event event) {
super(cause, responsiblePlugin);
this.listener = checkNotNull(listener, "listener");
this.event = checkNotNull(event, "event");
}
protected ServerEventException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin, Listener listener, Event event) {
super(message, cause, enableSuppression, writableStackTrace, responsiblePlugin);
this.listener = checkNotNull(listener, "listener");
this.event = checkNotNull(event, "event");
}
/**
* Gets the listener which threw the exception
*
* @return event listener
*/
public Listener getListener() {
return listener;
}
/**
* Gets the event which caused the exception
*
* @return event
*/
public Event getEvent() {
return event;
}
}

View File

@ -0,0 +1,23 @@
package org.github.paperspigot.exception;
/**
* Wrapper exception for all exceptions that are thrown by the server.
*/
public class ServerException extends Exception {
public ServerException(String message) {
super(message);
}
public ServerException(String message, Throwable cause) {
super(message, cause);
}
public ServerException(Throwable cause) {
super(cause);
}
protected ServerException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@ -0,0 +1,35 @@
package org.github.paperspigot.exception;
import org.bukkit.Bukkit;
import org.bukkit.entity.ThrownExpBottle;
import org.github.paperspigot.event.ServerExceptionEvent;
/**
* Thrown when the internal server throws a recoverable exception.
*/
public class ServerInternalException extends ServerException {
public ServerInternalException(String message) {
super(message);
}
public ServerInternalException(String message, Throwable cause) {
super(message, cause);
}
public ServerInternalException(Throwable cause) {
super(cause);
}
protected ServerInternalException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public static void reportInternalException(Throwable cause) {
try {
Bukkit.getPluginManager().callEvent(new ServerExceptionEvent(new ServerInternalException(cause)));;
} catch (Throwable t) {
t.printStackTrace(); // Don't want to rethrow!
}
}
}

View File

@ -0,0 +1,20 @@
package org.github.paperspigot.exception;
import org.bukkit.plugin.Plugin;
/**
* Thrown whenever there is an exception with any enabling or disabling of plugins.
*/
public class ServerPluginEnableDisableException extends ServerPluginException {
public ServerPluginEnableDisableException(String message, Throwable cause, Plugin responsiblePlugin) {
super(message, cause, responsiblePlugin);
}
public ServerPluginEnableDisableException(Throwable cause, Plugin responsiblePlugin) {
super(cause, responsiblePlugin);
}
protected ServerPluginEnableDisableException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin) {
super(message, cause, enableSuppression, writableStackTrace, responsiblePlugin);
}
}

View File

@ -0,0 +1,39 @@
package org.github.paperspigot.exception;
import com.google.common.base.Preconditions;
import org.apache.commons.lang.Validate;
import org.bukkit.plugin.Plugin;
import static com.google.common.base.Preconditions.*;
/**
* Wrapper exception for all cases to which a plugin can be immediately blamed for
*/
public class ServerPluginException extends ServerException {
public ServerPluginException(String message, Throwable cause, Plugin responsiblePlugin) {
super(message, cause);
this.responsiblePlugin = checkNotNull(responsiblePlugin, "responsiblePlugin");
}
public ServerPluginException(Throwable cause, Plugin responsiblePlugin) {
super(cause);
this.responsiblePlugin = checkNotNull(responsiblePlugin, "responsiblePlugin");
}
protected ServerPluginException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin) {
super(message, cause, enableSuppression, writableStackTrace);
this.responsiblePlugin = checkNotNull(responsiblePlugin, "responsiblePlugin");
}
private final Plugin responsiblePlugin;
/**
* Gets the plugin which is directly responsible for the exception being thrown
*
* @return plugin which is responsible for the exception throw
*/
public Plugin getResponsiblePlugin() {
return responsiblePlugin;
}
}

View File

@ -0,0 +1,61 @@
package org.github.paperspigot.exception;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import static com.google.common.base.Preconditions.*;
/**
* Thrown when an incoming plugin message channel throws an exception
*/
public class ServerPluginMessageException extends ServerPluginException {
private final Player player;
private final String channel;
private final byte[] data;
public ServerPluginMessageException(String message, Throwable cause, Plugin responsiblePlugin, Player player, String channel, byte[] data) {
super(message, cause, responsiblePlugin);
this.player = checkNotNull(player, "player");
this.channel = checkNotNull(channel, "channel");
this.data = checkNotNull(data, "data");
}
public ServerPluginMessageException(Throwable cause, Plugin responsiblePlugin, Player player, String channel, byte[] data) {
super(cause, responsiblePlugin);
this.player = checkNotNull(player, "player");
this.channel = checkNotNull(channel, "channel");
this.data = checkNotNull(data, "data");
}
protected ServerPluginMessageException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Plugin responsiblePlugin, Player player, String channel, byte[] data) {
super(message, cause, enableSuppression, writableStackTrace, responsiblePlugin);
this.player = checkNotNull(player, "player");
this.channel = checkNotNull(channel, "channel");
this.data = checkNotNull(data, "data");
}
/**
* Gets the channel to which the error occurred from recieving data from
* @return exception channel
*/
public String getChannel() {
return channel;
}
/**
* Gets the data to which the error occurred from
* @return exception data
*/
public byte[] getData() {
return data;
}
/**
* Gets the player which the plugin message causing the exception originated from
* @return exception player
*/
public Player getPlayer() {
return player;
}
}

View File

@ -0,0 +1,37 @@
package org.github.paperspigot.exception;
import org.bukkit.scheduler.BukkitTask;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Thrown when a plugin's scheduler fails with an exception
*/
public class ServerSchedulerException extends ServerPluginException {
private final BukkitTask task;
public ServerSchedulerException(String message, Throwable cause, BukkitTask task) {
super(message, cause, task.getOwner());
this.task = checkNotNull(task, "task");
}
public ServerSchedulerException(Throwable cause, BukkitTask task) {
super(cause, task.getOwner());
this.task = checkNotNull(task, "task");
}
protected ServerSchedulerException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, BukkitTask task) {
super(message, cause, enableSuppression, writableStackTrace, task.getOwner());
this.task = checkNotNull(task, "task");
}
/**
* Gets the task which threw the exception
* @return exception throwing task
*/
public BukkitTask getTask() {
return task;
}
}

View File

@ -0,0 +1,22 @@
package org.github.paperspigot.exception;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
/**
* Called when a tab-complete request throws an exception
*/
public class ServerTabCompleteException extends ServerCommandException {
public ServerTabCompleteException(String message, Throwable cause, Command command, CommandSender commandSender, String[] arguments) {
super(message, cause, command, commandSender, arguments);
}
public ServerTabCompleteException(Throwable cause, Command command, CommandSender commandSender, String[] arguments) {
super(cause, command, commandSender, arguments);
}
protected ServerTabCompleteException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Command command, CommandSender commandSender, String[] arguments) {
super(message, cause, enableSuppression, writableStackTrace, command, commandSender, arguments);
}
}

View File

@ -95,7 +95,7 @@
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.14</version>
<version>8.0.29</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
@ -143,6 +143,12 @@
<artifactId>zstd-jni</artifactId>
<version>1.5.2-3</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.2</version>
<scope>compile</scope>
</dependency>
</dependencies>
<!-- required until fixed plexus-compiler-eclipse is deployed -->

View File

@ -165,11 +165,7 @@ public class Block {
}
public int toLegacyData(IBlockData iblockdata) {
if (iblockdata != null && !iblockdata.a().isEmpty()) {
throw new IllegalArgumentException("Don\'t know how to convert " + iblockdata + " back into data...");
} else {
return 0;
}
return 0; // Sportpaper - optimize toLegacyData removing unneeded sanity checks
}
public IBlockData updateState(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {

View File

@ -0,0 +1,88 @@
package net.minecraft.server;
import java.util.List;
public class BlockCarpet extends Block {
public static final BlockStateEnum<EnumColor> COLOR = BlockStateEnum.of("color", EnumColor.class);
protected BlockCarpet() {
super(Material.WOOL);
this.j(this.blockStateList.getBlockData().set(BlockCarpet.COLOR, EnumColor.WHITE));
this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.0625F, 1.0F);
this.a(true);
this.a(CreativeModeTab.c);
this.b(0);
}
public MaterialMapColor g(IBlockData iblockdata) {
return iblockdata.get(BlockCarpet.COLOR).e();
}
public boolean c() {
return false;
}
public boolean d() {
return false;
}
public void j() {
this.b(0);
}
public void updateShape(IBlockAccess iblockaccess, BlockPosition blockposition) {
this.b(0);
}
protected void b(int i) {
this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.0625F, 1.0F);
}
// SportPaper start - No height on carpet in feet height, avoid 1.7 players glitching
public void a(World world, BlockPosition blockposition, IBlockData iblockdata, AxisAlignedBB axisalignedbb, List<AxisAlignedBB> list, Entity entity) {
if (entity instanceof EntityHuman && blockposition.getY() == (int) entity.getBoundingBox().b) {
return;
}
super.a(world, blockposition, iblockdata, axisalignedbb, list, entity);
}
// SportPaper end
public boolean canPlace(World world, BlockPosition blockposition) {
return super.canPlace(world, blockposition) && this.e(world, blockposition);
}
public void doPhysics(World world, BlockPosition blockposition, IBlockData iblockdata, Block block) {
this.e(world, blockposition, iblockdata);
}
private boolean e(World world, BlockPosition blockposition, IBlockData iblockdata) {
if (!this.e(world, blockposition)) {
this.b(world, blockposition, iblockdata, 0);
world.setAir(blockposition);
return false;
} else {
return true;
}
}
private boolean e(World world, BlockPosition blockposition) {
return !world.isEmpty(blockposition.down());
}
public int getDropData(IBlockData iblockdata) {
return iblockdata.get(BlockCarpet.COLOR).getColorIndex();
}
public IBlockData fromLegacyData(int i) {
return this.getBlockData().set(BlockCarpet.COLOR, EnumColor.fromColorIndex(i));
}
public int toLegacyData(IBlockData iblockdata) {
return iblockdata.get(BlockCarpet.COLOR).getColorIndex();
}
protected BlockStateList getStateList() {
return new BlockStateList(this, BlockCarpet.COLOR);
}
}

View File

@ -176,6 +176,7 @@ public class BlockFire extends Block {
}
BlockPosition blockposition1 = blockposition.a(j, l, k);
if (!world.isLoaded(blockposition1)) continue; // Paper
int j1 = this.m(world, blockposition1);
if (j1 > 0) {
@ -244,10 +245,13 @@ public class BlockFire extends Block {
}
private void a(World world, BlockPosition blockposition, int i, Random random, int j) {
// Paper start
final IBlockData iblockdata = world.getTypeIfLoaded(blockposition);
if (iblockdata == null) return;
int k = this.c(world.getType(blockposition).getBlock());
if (random.nextInt(i) < k) {
IBlockData iblockdata = world.getType(blockposition);
//IBlockData iblockdata = world.getType(blockposition); // Paper
// CraftBukkit start
org.bukkit.block.Block theBlock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
@ -300,7 +304,11 @@ public class BlockFire extends Block {
EnumDirection[] aenumdirection = EnumDirection.values();
int j = aenumdirection.length;
for (EnumDirection enumdirection : aenumdirection) {
for (int k = 0; k < j; ++k) {
EnumDirection enumdirection = aenumdirection[k];
final IBlockData type = world.getTypeIfLoaded(blockposition.shift(enumdirection)); // Paper
if (type == null) continue; // Paper
i = Math.max(this.d(world.getType(blockposition.shift(enumdirection)).getBlock()), i);
}

View File

@ -107,6 +107,7 @@ public class BlockFlowing extends BlockFluids {
if (this.h(world, blockposition.down(), iblockdata2)) {
// CraftBukkit start - Send "down" to the server
if (!canFlowTo(world, source, BlockFace.DOWN)) { return; } // Paper
BlockFromToEvent event = new BlockFromToEvent(source, BlockFace.DOWN);
if (server != null) {
server.getPluginManager().callEvent(event);
@ -141,6 +142,8 @@ public class BlockFlowing extends BlockFluids {
EnumDirection enumdirection1 = (EnumDirection) value;
// CraftBukkit start
if(this.h(world, blockposition.shift(enumdirection1), world.getType(blockposition.shift(enumdirection1)))) {
if (!canFlowTo(world, source, org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(enumdirection1))) { continue; } // Paper
BlockFromToEvent event = new BlockFromToEvent(source, org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(enumdirection1));
if (server != null) {
server.getPluginManager().callEvent(event);
@ -149,14 +152,21 @@ public class BlockFlowing extends BlockFluids {
if (!event.isCancelled()) {
this.flow(world, blockposition.shift(enumdirection1), world.getType(blockposition.shift(enumdirection1)), k);
}
}
// CraftBukkit end
}
}
}
// Paper start
private boolean canFlowTo(World world, org.bukkit.block.Block source, BlockFace face) {
return source.getWorld().isChunkLoaded((source.getX() + face.getModX()) >> 4, (source.getZ() + face.getModZ()) >> 4);
}
// Paper end
private void flow(World world, BlockPosition blockposition, IBlockData iblockdata, int i) {
if (world.isLoaded(blockposition) && this.h(world, blockposition, iblockdata)) { // CraftBukkit - add isLoaded check
if (/*world.isLoaded(blockposition) &&*/ this.h(world, blockposition, iblockdata)) { // CraftBukkit - add isLoaded check // Paper - Already checked before we get here for isLoade
if (iblockdata.getBlock() != Blocks.AIR) {
if (this.material == Material.LAVA) {
this.fizz(world, blockposition);

View File

@ -117,13 +117,13 @@ public class BlockPiston extends Block {
}
public boolean a(World world, BlockPosition blockposition, IBlockData iblockdata, int i, int j) {
EnumDirection enumdirection = iblockdata.get(BlockPiston.FACING);
EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockPiston.FACING);
if (!world.isClientSide) {
boolean flag = this.a(world, blockposition, enumdirection);
if (flag && i == 1) {
world.setTypeAndData(blockposition, iblockdata.set(BlockPiston.EXTENDED, Boolean.TRUE), 2);
world.setTypeAndData(blockposition, iblockdata.set(BlockPiston.EXTENDED, Boolean.valueOf(true)), 2);
return false;
}
@ -133,11 +133,15 @@ public class BlockPiston extends Block {
}
if (i == 0) {
// SportBukkit start
org.bukkit.event.block.BlockPistonEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPistonEvent(world, blockposition, enumdirection, true);
if(event != null && event.isCancelled()) return false;
// SportBukkit end
if (!this.a(world, blockposition, enumdirection, true)) {
return false;
}
world.setTypeAndData(blockposition, iblockdata.set(BlockPiston.EXTENDED, Boolean.TRUE), 2);
world.setTypeAndData(blockposition, iblockdata.set(BlockPiston.EXTENDED, Boolean.valueOf(true)), 2);
world.makeSound((double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D, "tile.piston.out", 0.5F, world.random.nextFloat() * 0.25F + 0.6F);
} else if (i == 1) {
TileEntity tileentity = world.getTileEntity(blockposition.shift(enumdirection));
@ -148,6 +152,10 @@ public class BlockPiston extends Block {
world.setTypeAndData(blockposition, Blocks.PISTON_EXTENSION.getBlockData().set(BlockPistonMoving.FACING, enumdirection).set(BlockPistonMoving.TYPE, this.sticky ? BlockPistonExtension.EnumPistonType.STICKY : BlockPistonExtension.EnumPistonType.DEFAULT), 3);
world.setTileEntity(blockposition, BlockPistonMoving.a(this.fromLegacyData(j), enumdirection, false, true));
// SportBukkit start
org.bukkit.event.block.BlockPistonEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPistonEvent(world, blockposition, enumdirection, false);
if(event != null && event.isCancelled()) return false;
// SportBukkit end
if (this.sticky) {
BlockPosition blockposition1 = blockposition.a(enumdirection.getAdjacentX() * 2, enumdirection.getAdjacentY() * 2, enumdirection.getAdjacentZ() * 2);
Block block = world.getType(blockposition1).getBlock();

View File

@ -18,6 +18,7 @@ import org.apache.logging.log4j.Logger;
import com.google.common.collect.Lists; // CraftBukkit
import org.bukkit.Bukkit; // CraftBukkit
import org.bukkit.craftbukkit.util.LongHash;
public class Chunk {
@ -29,6 +30,7 @@ public class Chunk {
private boolean h;
public final World world;
public final int[] heightMap;
public final long chunkKey; // Paper
public final int locX;
public final int locZ;
private boolean k;
@ -42,7 +44,7 @@ public class Chunk {
private boolean done;
private boolean lit;
private boolean p;
public boolean q;
public boolean q; public void markDirty() { this.q = true; }// Paper
private boolean r;
private long lastSaved;
private int t;
@ -155,6 +157,7 @@ public class Chunk {
this.world = world;
this.locX = i;
this.locZ = j;
this.chunkKey = LongHash.toLong(this.locX, this.locZ); // Paper
this.heightMap = new int[256];
for (int k = 0; k < this.entitySlices.length; ++k) {
@ -843,6 +846,7 @@ public class Chunk {
return !block.isTileEntity() ? null : ((IContainer) block).a(this.world, this.c(blockposition));
}
public final TileEntity getTileEntityImmediately(BlockPosition pos) { return this.a(pos, EnumTileEntityState.IMMEDIATE); } // Paper - OBFHELPER
public TileEntity a(BlockPosition blockposition, Chunk.EnumTileEntityState chunk_enumtileentitystate) {
// CraftBukkit start
TileEntity tileentity = null;
@ -899,9 +903,16 @@ public class Chunk {
System.out.println("Chunk coordinates: " + (this.locX * 16) + "," + (this.locZ * 16));
new Exception().printStackTrace();
// CraftBukkit end
if (this.world.paperSpigotConfig.removeCorruptTEs) {
this.removeTileEntity(tileentity.getPosition());
this.markDirty();
org.bukkit.Bukkit.getLogger().info("Removing corrupt tile entity");
}
}
}
public void removeTileEntity(BlockPosition blockposition) { this.e(blockposition); } // Paper - OBFHELPER
public void e(BlockPosition blockposition) {
if (this.h) {
TileEntity tileentity = this.tileEntities.remove(blockposition);

View File

@ -4,6 +4,7 @@ import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import it.unimi.dsi.fastutil.longs.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -16,38 +17,39 @@ import org.bukkit.craftbukkit.util.LongHash;
import org.bukkit.event.world.ChunkUnloadEvent;
// CraftBukkit end
// TacoSpigot start
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongSet;
// TacoSpigot end
public class ChunkProviderServer implements IChunkProvider {
private static final Logger b = LogManager.getLogger();
public LongSet unloadQueue = new LongArraySet(); // CraftBukkit - LongHashSet // TacoSpigot - LongHashSet -> HashArraySet
public LongSet unloadQueue = new LongOpenHashSet(20); // CraftBukkit - LongHashSet // TacoSpigot - LongHashSet -> HashArraySet
public Chunk emptyChunk;
public IChunkProvider chunkProvider;
public IChunkLoader chunkLoader; // KigPaper - private -> public
public boolean forceChunkLoad = false; // CraftBukkit - true -> false
public Long2ObjectMap<Chunk> chunks = new Long2ObjectOpenHashMap<>(4096, 0.5f); // TacoSpigot - use trove Long2ObjectOpenHashMap instead of craftbukkit implementation (using inital capacity and load factor chosen by Amaranth in an old impl)
// Paper start
protected Chunk lastChunkByPos = null;
public Long2ObjectMap<Chunk> chunks = new Long2ObjectOpenHashMap<Chunk>(8192, 0.5f) {
@Override
public Chunk get(long key) {
if (lastChunkByPos != null && key == lastChunkByPos.chunkKey) {
return lastChunkByPos;
}
return lastChunkByPos = super.get(key);
}
@Override
public Chunk remove(long key) {
if (lastChunkByPos != null && key == lastChunkByPos.chunkKey) {
lastChunkByPos = null;
}
return super.remove(key);
}
}; // CraftBukkit
// Paper end
public WorldServer world;
// Migot start
private ChunkRegionLoader checkedRegionLoader = null;
public boolean doesChunkExist(int x, int z) {
if(this.checkedRegionLoader == null && this.chunkLoader instanceof ChunkRegionLoader) {
this.checkedRegionLoader = (ChunkRegionLoader) this.chunkLoader;
}
if(this.checkedRegionLoader != null) {
return this.checkedRegionLoader.chunkExists(this.world, x, z);
}
return false;
}
// Migot end
public ChunkProviderServer(WorldServer worldserver, IChunkLoader ichunkloader, IChunkProvider ichunkprovider) {
this.emptyChunk = new EmptyChunk(worldserver, Integer.MIN_VALUE, Integer.MIN_VALUE); // Migot
this.world = worldserver;
@ -126,7 +128,6 @@ public class ChunkProviderServer implements IChunkProvider {
}
public Chunk getChunkAt(int i, int j, Runnable runnable) {
unloadQueue.remove(LongHash.toLong(i, j)); // TacoSpigot - directly invoke LongHash
Chunk chunk = chunks.get(LongHash.toLong(i, j));
ChunkRegionLoader loader = null;
@ -146,6 +147,7 @@ public class ChunkProviderServer implements IChunkProvider {
chunk = originalGetChunkAt(i, j);
}
unloadQueue.remove(LongHash.toLong(i, j)); // SportPaper
// If we didn't load the chunk async and have a callback run it now
if (runnable != null) {
runnable.run();
@ -154,7 +156,6 @@ public class ChunkProviderServer implements IChunkProvider {
return chunk;
}
public Chunk originalGetChunkAt(int i, int j) {
this.unloadQueue.remove(LongHash.toLong(i, j)); // TacoSpigot - directly invoke LongHash
Chunk chunk = this.chunks.get(LongHash.toLong(i, j));
boolean newChunk = false;
// CraftBukkit end
@ -215,6 +216,7 @@ public class ChunkProviderServer implements IChunkProvider {
world.timings.syncChunkLoadTimer.stopTiming(); // Spigot
}
this.unloadQueue.remove(LongHash.toLong(i, j)); // SportPaper
return chunk;
}
@ -365,33 +367,31 @@ public class ChunkProviderServer implements IChunkProvider {
}
public boolean unloadChunks() {
if (!this.world.savingDisabled) {
// CraftBukkit start
Server server = this.world.getServer();
// TacoSpigot start - use iterator for unloadQueue
LongIterator iterator = unloadQueue.iterator();
for (int i = 0; i < 100 && iterator.hasNext(); ++i) {
long chunkcoordinates = iterator.next();
iterator.remove();
// TacoSpigot end
Chunk chunk = this.chunks.get(chunkcoordinates);
if (chunk == null) continue;
// SportPaper start
public void unloadAllChunks() {
for(Chunk chunk : chunks.values()) {
unloadChunk(chunk);
}
}
public void unloadChunk(Chunk chunk) {
unloadChunk(chunk, false);
}
private void unloadChunk(Chunk chunk, boolean auto) {
Server server = this.world.getServer();
ChunkUnloadEvent event = new ChunkUnloadEvent(chunk.bukkitChunk);
server.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
if (chunk != null) {
chunk.markAsUnloaded(); // Migot
chunk.removeEntities();
this.saveChunk(chunk);
this.saveChunkNOP(chunk);
this.chunks.remove(chunkcoordinates); // CraftBukkit
this.chunks.remove(chunk.chunkKey); // CraftBukkit
if (!auto && this.unloadQueue.contains(chunk.chunkKey)) {
this.unloadQueue.remove(chunk.chunkKey);
}
// this.unloadQueue.remove(olong);
// Update neighbor counts
for (int x = -2; x < 3; x++) {
for (int z = -2; z < 3; z++) {
@ -408,6 +408,22 @@ public class ChunkProviderServer implements IChunkProvider {
}
}
}
// SportPaper end
public boolean unloadChunks() {
if (!this.world.savingDisabled) {
// CraftBukkit start
Server server = this.world.getServer();
// SportPaper start
LongIterator iterator = unloadQueue.iterator();
for (int i = 0; i < 100 && iterator.hasNext(); ++i) {
long chunkcoordinates = iterator.nextLong();
iterator.remove();
// SportPaper end
Chunk chunk = this.chunks.get(chunkcoordinates);
if (chunk == null) continue;
unloadChunk(chunk, true); // SportPaper - Move to own method
}
// CraftBukkit end
if (this.chunkLoader != null) {

View File

@ -8,6 +8,7 @@ import java.net.InetAddress;
import java.net.Proxy;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
@ -28,7 +29,7 @@ import org.bukkit.event.server.RemoteServerCommandEvent;
public class DedicatedServer extends MinecraftServer implements IMinecraftServer {
private static final Logger LOGGER = LogManager.getLogger();
private final List<ServerCommand> l = Collections.synchronizedList(Lists.<ServerCommand>newArrayList()); // CraftBukkit - fix decompile error
private final Queue<ServerCommand> l = new java.util.concurrent.ConcurrentLinkedQueue<>(); // Paper - use a proper queue
private RemoteStatusListener m;
private RemoteControlListener n;
public PropertyManager propertyManager;
@ -374,7 +375,7 @@ public class DedicatedServer extends MinecraftServer implements IMinecraftServer
public void B() { // CraftBukkit - fix decompile error
super.B();
this.aO();
// this.aO(); // SportBukkit - moved to processTasks()
}
public boolean getAllowNether() {
@ -399,10 +400,21 @@ public class DedicatedServer extends MinecraftServer implements IMinecraftServer
this.l.add(new ServerCommand(s, icommandlistener));
}
// SportBukkit start
@Override
protected void processTasks() {
super.processTasks();
processCommands();
}
// SportBukkit end
public void processCommands() { aO(); } // SportBukkit - alias
public void aO() {
SpigotTimings.serverCommandTimer.startTiming(); // Spigot
while (!this.l.isEmpty()) {
ServerCommand servercommand = this.l.remove(0);
// Paper start - use proper queue
ServerCommand servercommand;
while ((servercommand = this.l.poll()) != null) {
// Paper end
// CraftBukkit start - ServerCommand for preprocessing
ServerCommandEvent event = new ServerCommandEvent(console, servercommand.command);
@ -652,7 +664,7 @@ public class DedicatedServer extends MinecraftServer implements IMinecraftServer
return RemoteControlCommandListener.getInstance().j();
}
};
processQueue.add(waitable);
addMainThreadTask(waitable);
try {
return waitable.get();
} catch (java.util.concurrent.ExecutionException e) {

View File

@ -17,6 +17,7 @@ import org.bukkit.entity.Painting;
import org.bukkit.entity.Vehicle;
import co.aikar.timings.SpigotTimings; // Spigot
import co.aikar.timings.Timing; // Spigot
import org.bukkit.event.entity.EntityCombustByBlockEvent;
import org.bukkit.event.entity.EntityCombustByEntityEvent;
import org.bukkit.event.hanging.HangingBreakByEntityEvent;
import org.bukkit.event.painting.PaintingBreakByEntityEvent;
@ -50,7 +51,7 @@ public abstract class Entity implements ICommandListener {
private static int entityCount = 1;
private int id;
public double j;
public boolean k;
public boolean k; public boolean blocksEntitySpawning() { return k; } // Paper - OBFHELPER
public Entity passenger;
public Entity vehicle;
public boolean attachedToPlayer;
@ -94,6 +95,7 @@ public abstract class Entity implements ICommandListener {
public int ticksLived;
public int maxFireTicks;
public int fireTicks;
public boolean wasOnFire; // CraftBukkit - to detect when the fire goes out
public boolean inWater; // Spigot - protected -> public // PAIL
public int noDamageTicks;
protected boolean justCreated;
@ -117,7 +119,7 @@ public abstract class Entity implements ICommandListener {
public boolean ah;
public boolean ai;
public int portalCooldown;
protected boolean ak;
protected boolean ak; public boolean inPortal() { return ak; } // Paper - OBFHELPER
protected int al;
public int dimension;
@ -394,22 +396,24 @@ public abstract class Entity implements ICommandListener {
this.damageEntity(DamageSource.LAVA, 4.0F);
// CraftBukkit start - Fallen in lava TODO: this event spams!
Vec3D lavaPos = this.world.getLargestBlockIntersection(this.boundingBox.shrink(0.001D, 0.001D, 0.001D), Material.LAVA);
org.bukkit.block.Block lavaBlock = lavaPos == null ? null : this.world.getWorld().getBlockAt((int) lavaPos.a, (int) lavaPos.b, (int) lavaPos.c);
try {
CraftEventFactory.blockDamage = lavaBlock;
this.damageEntity(DamageSource.LAVA, 4);
} finally {
CraftEventFactory.blockDamage = null;
}
if (this instanceof EntityLiving) {
if (fireTicks <= 0) {
// not on fire yet
// TODO: shouldn't be sending null for the block
org.bukkit.block.Block damager = null; // ((WorldServer) this.l).getWorld().getBlockAt(i, j, k);
// Note that in order for cancelling or custom duration to work properly,
// this event must be fired every tick, thus we cannot avoid "spamming" it.
org.bukkit.entity.Entity damagee = this.getBukkitEntity();
EntityCombustEvent combustEvent = new org.bukkit.event.entity.EntityCombustByBlockEvent(damager, damagee, 15);
EntityCombustEvent combustEvent = new org.bukkit.event.entity.EntityCombustByBlockEvent(lavaBlock, damagee, 15);
this.world.getServer().getPluginManager().callEvent(combustEvent);
if (!combustEvent.isCancelled()) {
this.setOnFire(combustEvent.getDuration());
}
} else {
// This will be called every single tick the entity is in lava, so don't throw an event
this.setOnFire(15);
}
return;
}
// CraftBukkit end - we also don't throw an event unless the object in lava is living, to save on some event calls
@ -802,21 +806,32 @@ public abstract class Entity implements ICommandListener {
boolean flag2 = this.U();
if (this.world.e(this.getBoundingBox().shrink(0.001D, 0.001D, 0.001D))) {
// CraftBukkit start - get the location of the fire block
Vec3D firePos = this.world.getLargestBlockIntersection(this.boundingBox.shrink(0.001D, 0.001D, 0.001D), Material.FIRE);
if (firePos != null) {
org.bukkit.block.Block fireBlock = this.bukkitEntity.getWorld().getBlockAt((int) firePos.a, (int) firePos.b, (int) firePos.c);
try {
CraftEventFactory.blockDamage = fireBlock;
this.burn(1);
} finally {
CraftEventFactory.blockDamage = null;
}
if (!flag2) {
++this.fireTicks;
// CraftBukkit start - Not on fire yet
if (this.fireTicks <= 0) { // Only throw events on the first combust, otherwise it spams
EntityCombustEvent event = new EntityCombustEvent(getBukkitEntity(), 8);
world.getServer().getPluginManager().callEvent(event);
EntityCombustByBlockEvent event = new EntityCombustByBlockEvent(fireBlock, this.getBukkitEntity(), 8);
this.world.getServer().getPluginManager().callEvent(event);
if (!event.isCancelled()) {
setOnFire(event.getDuration());
// Note carefully how this works: when fireTicks is negative, the entity is
// "heating up" but not on fire yet. When fireTicks reaches 0, the entity
// "ignites" and fireTicks jumps to 160. It will then stay at that value as
// long as the player remains in fire (because the ++ below will cancel out
// the -- in the entity tick). For the event cancelling to work, it has to
// be fired every tick, thus we cannot avoid "spamming" it.
++this.fireTicks;
if (this.fireTicks == 0) {
this.setOnFire(event.getDuration());
}
} else {
// CraftBukkit end
this.setOnFire(8);
}
}
} else if (this.fireTicks <= 0) {
@ -828,6 +843,14 @@ public abstract class Entity implements ICommandListener {
this.fireTicks = -this.maxFireTicks;
}
// CraftBukkit start
if(this.fireTicks > 0) {
this.wasOnFire = true;
} else if(this.wasOnFire && this.fireTicks <= 0) {
this.wasOnFire = false;
}
// CraftBukkit end
this.world.methodProfiler.b();
}
}
@ -1139,6 +1162,7 @@ public abstract class Entity implements ICommandListener {
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.setYawPitch(f, f1);
}
@ -2082,7 +2106,7 @@ public abstract class Entity implements ICommandListener {
}
public void teleportTo(Location exit, boolean portal) {
if (true) {
if (!this.dead) { // Paper
WorldServer worldserver = ((CraftWorld) getBukkitEntity().getLocation().getWorld()).getHandle();
WorldServer worldserver1 = ((CraftWorld) exit.getWorld()).getHandle();
// CraftBukkit end

View File

@ -11,6 +11,7 @@ import org.bukkit.event.player.PlayerPickupItemEvent;
import net.techcable.tacospigot.event.entity.ArrowCollideEvent;
import org.bukkit.Bukkit;
import org.bukkit.entity.Arrow;
import org.github.paperspigot.PaperSpigotConfig;
// TacoSpigot end
public class EntityArrow extends Entity implements IProjectile {
@ -101,7 +102,7 @@ public class EntityArrow extends Entity implements IProjectile {
this.motX = -MathHelper.sin(this.yaw / 180.0F * 3.1415927F) * MathHelper.cos(this.pitch / 180.0F * 3.1415927F);
this.motZ = MathHelper.cos(this.yaw / 180.0F * 3.1415927F) * MathHelper.cos(this.pitch / 180.0F * 3.1415927F);
this.motY = -MathHelper.sin(this.pitch / 180.0F * 3.1415927F);
this.shoot(this.motX, this.motY, this.motZ, f * 1.5F, 1.0F);
this.shoot(this.motX, this.motY, this.motZ, f * 1.5F, PaperSpigotConfig.includeRandomnessInArrowTrajectory ? 1.0F : 0); // SportPaper
}
protected void h() {
@ -114,9 +115,11 @@ public class EntityArrow extends Entity implements IProjectile {
d0 /= f2;
d1 /= f2;
d2 /= f2;
if (f1 != 0) {
d0 += this.random.nextGaussian() * (double) (this.random.nextBoolean() ? -1 : 1) * 0.007499999832361937D * (double) f1;
d1 += this.random.nextGaussian() * (double) (this.random.nextBoolean() ? -1 : 1) * 0.007499999832361937D * (double) f1;
d2 += this.random.nextGaussian() * (double) (this.random.nextBoolean() ? -1 : 1) * 0.007499999832361937D * (double) f1;
}
d0 *= f;
d1 *= f;
d2 *= f;
@ -248,7 +251,7 @@ public class EntityArrow extends Entity implements IProjectile {
f2 = MathHelper.sqrt(this.motX * this.motX + this.motY * this.motY + this.motZ * this.motZ);
int k = MathHelper.f((double) f2 * this.damage);
if (this.isCritical()) {
if (this.isCritical() && PaperSpigotConfig.includeRandomnessInArrowDamage) { // SportPaper
k += this.random.nextInt(k / 2 + 2);
}

View File

@ -106,7 +106,7 @@ public class EntityBoat extends Entity {
this.world.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return true;
return false;
}
// f = event.getDamage(); // TODO Why don't we do this?
// CraftBukkit end

View File

@ -569,8 +569,12 @@ public class EntityEnderDragon extends EntityInsentient implements IComplex, IMo
if (this.by == 1) {
// CraftBukkit start - Use relative location for far away sounds
// this.world.a(1018, new BlockPosition(this), 0);
int viewDistance = this.world.getServer().getViewDistance() * 16;
for (EntityPlayer player : MinecraftServer.getServer().getPlayerList().players) {
// Paper start
//int viewDistance = ((WorldServer) this.world).spigotConfig.viewDistance * 16; // Paper - updated to use worlds actual view distance incase we have to uncomment this due to removal of player view distance API
for (EntityHuman human : world.players) {
EntityPlayer player = (EntityPlayer) human;
int viewDistance = player.viewDistance;
// Paper end
double deltaX = this.locX - player.locX;
double deltaZ = this.locZ - player.locZ;
double distanceSquared = deltaX * deltaX + deltaZ * deltaZ;

View File

@ -361,6 +361,12 @@ public class EntityFishingHook extends Entity {
this.motY *= f2;
this.motZ *= f2;
this.setPosition(this.locX, this.locY, this.locZ);
// Paper start - These shouldn't be going through portals
if (this.inPortal()) {
this.die();
}
// Paper end
}
}
}

View File

@ -853,6 +853,9 @@ public abstract class EntityHuman extends EntityLiving {
public boolean a(EntityHuman entityhuman) {
// CraftBukkit start - Change to check OTHER player's scoreboard team according to API
// To summarize this method's logic, it's "Can parameter hurt this"
if(this == entityhuman) return true; // SportBukkit - self-damage is always allowed
org.bukkit.scoreboard.Team team;
if (entityhuman instanceof EntityPlayer) {
EntityPlayer thatPlayer = (EntityPlayer) entityhuman;

View File

@ -156,6 +156,10 @@ public class EntityItem extends Entity implements HopperPusher {
// Spigot end
private void w() {
// Paper start - avoid item merge if stack size above max stack size
ItemStack stack = getItemStack();
if (stack.count >= stack.getMaxStackSize()) return;
// Paper end
// Spigot start
double radius = world.spigotConfig.itemMerge;
// Spigot end

View File

@ -122,7 +122,7 @@ public abstract class EntityMinecartAbstract extends Entity implements INamableT
this.world.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return true;
return false;
}
f = (float) event.getDamage();

View File

@ -4,12 +4,8 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.mojang.authlib.GameProfile;
import io.netty.buffer.Unpooled;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.*;
import com.elevatemc.spigot.eSpigot;
import org.apache.logging.log4j.LogManager;
@ -31,13 +27,14 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
private static final Logger bH = LogManager.getLogger();
public String locale = "en_US"; // Spigot
public long lastSave = MinecraftServer.currentTick; // Paper
public PlayerConnection playerConnection;
public final MinecraftServer server;
public final PlayerInteractManager playerInteractManager;
public double d;
public double e;
public final List<ChunkCoordIntPair> chunkCoordIntPairQueue = Lists.newLinkedList();
public final List<Integer> removeQueue = Lists.newLinkedList();
public final Deque<Integer> removeQueue = new ArrayDeque<>(); // Paper
private final ServerStatisticManager bK;
private float bL = Float.MIN_VALUE;
private float bM = -1.0E8F;
@ -127,6 +124,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
public void a(NBTTagCompound nbttagcompound) {
super.a(nbttagcompound);
if (this.locY > 300) this.locY = 257; // Paper - bring down to a saner Y level if out of world
if (nbttagcompound.hasKeyOfType("playerGameType", 99)) {
if (MinecraftServer.getServer().getForceGamemode()) {
this.playerInteractManager.setGameMode(MinecraftServer.getServer().getGamemode());
@ -223,10 +221,11 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
Iterator iterator = this.removeQueue.iterator();
int j = 0;
while (iterator.hasNext() && j < i) {
aint[j++] = (Integer) iterator.next();
iterator.remove();
}
// Paper start
Integer integer;
while (j < i && (integer = this.removeQueue.poll()) != null) {
aint[j++] = integer.intValue();
}// Paper end
this.playerConnection.sendPacket(new PacketPlayOutEntityDestroy(aint));
}
@ -423,7 +422,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
}
IChatBaseComponent chatmessage = this.bs().b();
IChatBaseComponent chatmessage = damagesource == DamageSource.GENERIC ? damagesource.getLocalizedDeathMessage(this) : this.bs().b(); // CraftBukkit
String deathmessage = chatmessage.c();
org.bukkit.event.entity.PlayerDeathEvent event = CraftEventFactory.callPlayerDeathEvent(this, loot, deathmessage, keepInventory);
@ -464,7 +463,8 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
EntityLiving entityliving = this.bt();
if (entityliving != null) {
// CraftBukkit - can't have a combat tracked killer with a generic damage source
if (entityliving != null && damagesource != DamageSource.GENERIC) {
EntityTypes.MonsterEggInfo entitytypes_monsteregginfo = EntityTypes.eggInfo.get(EntityTypes.a(entityliving));
if (entitytypes_monsteregginfo != null) {
@ -488,7 +488,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
if (!flag && this.invulnerableTicks > 0 && damagesource != DamageSource.OUT_OF_WORLD) {
return false;
} else {
if (damagesource instanceof EntityDamageSource) {
if (damagesource instanceof EntityDamageSource && !damagesource.isExplosion()) { // SportBukkit - explosion damage is not subject to FF rules
Entity entity = damagesource.getEntity();
if (entity instanceof EntityHuman && !this.a((EntityHuman) entity)) {
@ -510,6 +510,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
public boolean a(EntityHuman entityhuman) {
if(this == entityhuman) return true; // SportBukkit - self-damage is always allowed
return this.cr() && super.a(entityhuman);
}
@ -917,8 +918,11 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.lastSentExp = -1;
this.bM = -1.0F;
this.bN = -1;
// Paper start - Optimize remove queue
if (this.removeQueue != ((EntityPlayer) entityhuman).removeQueue) {
this.removeQueue.addAll(((EntityPlayer) entityhuman).removeQueue);
}
}
protected void a(MobEffect mobeffect) {
super.a(mobeffect);
@ -1201,6 +1205,15 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.exp = 0;
this.deathTicks = 0;
this.removeAllEffects();
// Clear potion metadata now, because new effects might get added
// before the update in the tick has a chance to run, and if they
// match the old effects, the metadata will never be marked dirty
// and will go out of sync with the client.
this.datawatcher.watch(8, (byte) 0);
this.datawatcher.watch(7, 0);
this.setInvisible(false);
this.updateEffects = true;
this.activeContainer = this.defaultContainer;
this.killer = null;

View File

@ -153,7 +153,8 @@ public class EntityTrackerEntry {
this.v = 0;
// CraftBukkit start - Refresh list of who can see a player before sending teleport packet
if (this.tracker instanceof EntityPlayer) {
this.scanPlayers(new java.util.ArrayList(this.trackedPlayers));
// SportPaper - Fix invisibility on teleport
this.scanPlayers(new ArrayList<EntityHuman>(this.tracker.world.players));
}
// CraftBukkit end
object = new PacketPlayOutEntityTeleport(this.tracker.getId(), i, j, k, (byte) l, (byte) i1, this.tracker.onGround);
@ -272,7 +273,7 @@ public class EntityTrackerEntry {
DataWatcher datawatcher = this.tracker.getDataWatcher();
if (datawatcher.a()) {
if (eSpigotFeature.OBFUSCATE_HEALTH.isEnabled()) {
if (eSpigotFeature.OBFUSCATE_HEALTH.isEnabled() && this.tracker instanceof EntityHuman) {
List<DataWatcher.WatchableObject> changedMetadata = datawatcher.c();
Iterator<DataWatcher.WatchableObject> iter = changedMetadata.iterator();
boolean found = false;
@ -423,13 +424,15 @@ public class EntityTrackerEntry {
}
// CraftBukkit start - Fix for nonsensical head yaw
if(this.tracker instanceof EntityLiving) { // SportPaper - avoid processing entities that can't change head rotation
this.i = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F);
// KigPaper
if (this.tracker instanceof EntityLiving) {
this.i = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F);
// CraftBukkit what the fuck were you thinking?
//this.broadcast(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) i)); // KigPaper
// SportPaper start
// This was originally introduced by CraftBukkit, though the implementation is wrong since it's broadcasting
// the packet again in a method that is already called for each player. This would create a very serious performance issue
// with high player and entity counts (each sendPacket call involves waking up the event loop and flushing the network stream).
// this.broadcast(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) i));
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) i));
// SportPaper end
}
// CraftBukkit end

View File

@ -186,8 +186,12 @@ public class EntityWither extends EntityMonster implements IRangedEntity {
// CraftBukkit start - Use relative location for far away sounds
// this.world.a(1013, new BlockPosition(this), 0);
int viewDistance = this.world.getServer().getViewDistance() * 16;
for (EntityPlayer player : MinecraftServer.getServer().getPlayerList().players) {
// Paper start
//int viewDistance = ((WorldServer) this.world).spigotConfig.viewDistance * 16; // Paper - updated to use worlds actual view distance incase we have to uncomment this due to removal of player view distance API
for (EntityHuman human : world.players) {
EntityPlayer player = (EntityPlayer) human;
int viewDistance = player.viewDistance;
// Paper end
double deltaX = this.locX - player.locX;
double deltaZ = this.locZ - player.locZ;
double distanceSquared = deltaX * deltaX + deltaZ * deltaZ;

View File

@ -36,7 +36,8 @@ public class ItemBlock extends Item {
this.a.postPlace(world, blockposition, iblockdata1, entityhuman, itemstack);
}
world.makeSound((float) blockposition.getX() + 0.5F, (float) blockposition.getY() + 0.5F, (float) blockposition.getZ() + 0.5F, this.a.stepSound.getPlaceSound(), (this.a.stepSound.getVolume1() + 1.0F) / 2.0F, this.a.stepSound.getVolume2() * 0.8F);
// SPIGOT-1288
// world.makeSound((double) ((float) blockposition.getX() + 0.5F), (double) ((float) blockposition.getY() + 0.5F), (double) ((float) blockposition.getZ() + 0.5F), this.a.stepSound.getPlaceSound(), (this.a.stepSound.getVolume1() + 1.0F) / 2.0F, this.a.stepSound.getVolume2() * 0.8F);
--itemstack.count;
}

View File

@ -46,6 +46,7 @@ public class ItemBucket extends Item {
PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent(entityhuman, blockposition.getX(), blockposition.getY(), blockposition.getZ(), null, itemstack, Items.WATER_BUCKET);
if (event.isCancelled()) {
((EntityPlayer)entityhuman).updateInventory(entityhuman.defaultContainer);
return itemstack;
}
// CraftBukkit end
@ -59,6 +60,7 @@ public class ItemBucket extends Item {
PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent(entityhuman, blockposition.getX(), blockposition.getY(), blockposition.getZ(), null, itemstack, Items.LAVA_BUCKET);
if (event.isCancelled()) {
((EntityPlayer)entityhuman).updateInventory(entityhuman.defaultContainer);
return itemstack;
}
// CraftBukkit end
@ -72,6 +74,7 @@ public class ItemBucket extends Item {
PlayerBucketEmptyEvent event = CraftEventFactory.callPlayerBucketEmptyEvent(entityhuman, blockposition.getX(), blockposition.getY(), blockposition.getZ(), movingobjectposition.direction, itemstack);
if (event.isCancelled()) {
((EntityPlayer)entityhuman).updateInventory(entityhuman.defaultContainer);
return itemstack;
}
@ -86,12 +89,13 @@ public class ItemBucket extends Item {
}
// CraftBukkit start
// Check that the bucket can be emptied before firing the event
if (world.isEmpty(blockposition1) || !world.getType(blockposition1).getBlock().getMaterial().isBuildable()) {
PlayerBucketEmptyEvent event = CraftEventFactory.callPlayerBucketEmptyEvent(entityhuman, blockposition.getX(), blockposition.getY(), blockposition.getZ(), movingobjectposition.direction, itemstack);
if (event.isCancelled()) {
((EntityPlayer)entityhuman).updateInventory(entityhuman.defaultContainer);
return itemstack;
}
// CraftBukkit end
if (this.a(world, blockposition1) && !entityhuman.abilities.canInstantlyBuild) {
entityhuman.b(StatisticList.USE_ITEM_COUNT[Item.getId(this)]);
@ -106,6 +110,7 @@ public class ItemBucket extends Item {
}
return itemstack;
}
}
// PaperSpigot end
return CraftItemStack.asNMSCopy(event.getItemStack()); // CraftBukkit
}

View File

@ -218,6 +218,12 @@ public final class ItemStack {
}
}
// SPIGOT-1288 - play sound stripped from ItemBlock
if (this.getItem() instanceof ItemBlock) {
Block base = ((ItemBlock) this.getItem()).a;
world.makeSound((double) ((float) blockposition.getX() + 0.5F), (double) ((float) blockposition.getY() + 0.5F), (double) ((float) blockposition.getZ() + 0.5F), base.stepSound.getPlaceSound(), (base.stepSound.getVolume1() + 1.0F) / 2.0F, base.stepSound.getVolume2() * 0.8F);
}
entityhuman.b(StatisticList.USE_ITEM_COUNT[Item.getId(this.item)]);
}
}

View File

@ -48,8 +48,18 @@ public class LoginListener implements PacketLoginInListener, IUpdatePlayerListBo
}
public void c() {
// Paper start - Do not allow logins while the server is shutting down
if (!MinecraftServer.getServer().isRunning()) {
this.disconnect(org.spigotmc.SpigotConfig.restartMessage);
return;
}
// Paper end
if (this.g == LoginListener.EnumProtocolState.READY_TO_ACCEPT) {
// Paper start - prevent logins to be processed even though disconnect was called
if (networkManager.channel != null && networkManager.channel.isOpen()) {
this.b();
}
// Paper end
} else if (this.g == LoginListener.EnumProtocolState.e) {
EntityPlayer entityplayer = this.server.getPlayerList().a(this.i.getId());
@ -239,10 +249,41 @@ public class LoginListener implements PacketLoginInListener, IUpdatePlayerListBo
}
}
// Paper start - Delay async prelogin until plugins are ready
private static volatile Object blockingLogins = new Object();
public static void checkStartupAndBlock() {
final Object lock = LoginListener.blockingLogins;
if (lock != null) {
synchronized (lock) {
for (;;) {
if (LoginListener.blockingLogins == null) {
return;
}
try {
lock.wait();
} catch (final InterruptedException ignore) {
Thread.currentThread().interrupt();
}
}
}
}
}
public static void allowLogins() {
final Object lock = LoginListener.blockingLogins;
synchronized (lock) {
LoginListener.blockingLogins = null;
lock.notifyAll();
}
}
// Paper end
// Spigot start
public class LoginHandler {
public void fireEvents() throws Exception {
LoginListener.checkStartupAndBlock(); // Paper - Delay async login events until plugins are ready
String playerName = i.getName();
java.net.InetAddress address = ((java.net.InetSocketAddress) networkManager.getSocketAddress()).getAddress();
java.util.UUID uniqueId = i.getId();
@ -263,7 +304,7 @@ public class LoginListener implements PacketLoginInListener, IUpdatePlayerListBo
return event.getResult();
}};
LoginListener.this.server.processQueue.add(waitable);
LoginListener.this.server.addMainThreadTask(waitable);
if (waitable.get() != PlayerPreLoginEvent.Result.ALLOWED) {
disconnect(event.getKickMessage());
return;

View File

@ -0,0 +1,142 @@
package net.minecraft.server;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.bukkit.Location;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.util.Waitable;
import org.spigotmc.AsyncCatcher;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.function.Supplier;
public final class MCUtil {
private static final Executor asyncExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("Paper Async Task Handler Thread - %1$d").build());
private MCUtil() {}
/**
* Quickly generate a stack trace for current location
*
* @return Stacktrace
*/
public static String stack() {
return ExceptionUtils.getFullStackTrace(new Throwable());
}
/**
* Quickly generate a stack trace for current location with message
*
* @param str
* @return Stacktrace
*/
public static String stack(String str) {
return ExceptionUtils.getFullStackTrace(new Throwable(str));
}
public static <T> T ensureMain(String reason, Supplier<T> run) {
if (AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().primaryThread) {
new IllegalStateException( "Asynchronous " + reason + "! Blocking thread until it returns ").printStackTrace();
Waitable<T> wait = new Waitable<T>() {
@Override
protected T evaluate() {
return run.get();
}
};
MinecraftServer.getServer().addMainThreadTask(wait);
try {
return wait.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return null;
}
return run.get();
}
public static double distance(Entity e1, Entity e2) {
return Math.sqrt(distanceSq(e1, e2));
}
public static double distance(BlockPosition e1, BlockPosition e2) {
return Math.sqrt(distanceSq(e1, e2));
}
public static double distance(double x1, double y1, double z1, double x2, double y2, double z2) {
return Math.sqrt(distanceSq(x1, y1, z1, x2, y2, z2));
}
public static double distanceSq(Entity e1, Entity e2) {
return distanceSq(e1.locX,e1.locY,e1.locZ, e2.locX,e2.locY,e2.locZ);
}
public static double distanceSq(BlockPosition pos1, BlockPosition pos2) {
return distanceSq(pos1.getX(), pos1.getY(), pos1.getZ(), pos2.getX(), pos2.getY(), pos2.getZ());
}
public static double distanceSq(double x1, double y1, double z1, double x2, double y2, double z2) {
return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2);
}
public static Location toLocation(World world, double x, double y, double z) {
return new Location(world.getWorld(), x, y, z);
}
public static Location toLocation(World world, BlockPosition pos) {
return new Location(world.getWorld(), pos.getX(), pos.getY(), pos.getZ());
}
public static Location toLocation(Entity entity) {
return new Location(entity.getWorld().getWorld(), entity.locX, entity.locY, entity.locZ);
}
public static BlockPosition toBlockPosition(Location loc) {
return new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
}
public static boolean isEdgeOfChunk(BlockPosition pos) {
final int modX = pos.getX() & 15;
final int modZ = pos.getZ() & 15;
return (modX == 0 || modX == 15 || modZ == 0 || modZ == 15);
}
@Nullable
public static Chunk getLoadedChunkWithoutMarkingActive(World world, int x, int z) {
return ((ChunkProviderServer) world.chunkProvider).chunks.get(ChunkCoordIntPair.a(x, z));
}
@Nullable
public static Chunk getLoadedChunkWithoutMarkingActive(IChunkProvider provider, int x, int z) {
return ((ChunkProviderServer)provider).chunks.get(ChunkCoordIntPair.a(x, z));
}
public static void scheduleAsyncTask(Runnable run) {
asyncExecutor.execute(run);
}
@Nullable
public static TileEntityHopper getHopper(World world, BlockPosition pos) {
Chunk chunk = world.getChunkIfLoaded(pos.getX() >> 4, pos.getZ() >> 4);
if (chunk != null && chunk.getBlockData(pos).getBlock() == Blocks.HOPPER) {
TileEntity tileEntity = chunk.getTileEntityImmediately(pos);
if (tileEntity instanceof TileEntityHopper) {
return (TileEntityHopper) tileEntity;
}
}
return null;
}
@Nonnull
public static World getNMSWorld(@Nonnull org.bukkit.World world) {
return ((CraftWorld) world).getHandle();
}
public static World getNMSWorld(@Nonnull org.bukkit.entity.Entity entity) {
return getNMSWorld(entity.getWorld());
}
}

View File

@ -0,0 +1,81 @@
package net.minecraft.server;
import java.util.ArrayList;
public class MerchantRecipeList extends ArrayList<MerchantRecipe> {
public MerchantRecipeList() {}
public MerchantRecipeList(NBTTagCompound nbttagcompound) {
this.a(nbttagcompound);
}
public MerchantRecipe a(ItemStack itemstack, ItemStack itemstack1, int i) {
if (i > 0 && i < this.size()) {
MerchantRecipe merchantrecipe = this.get(i);
return this.a(itemstack, merchantrecipe.getBuyItem1()) && (itemstack1 == null && !merchantrecipe.hasSecondItem() || merchantrecipe.hasSecondItem() && this.a(itemstack1, merchantrecipe.getBuyItem2())) && itemstack.count >= merchantrecipe.getBuyItem1().count && (!merchantrecipe.hasSecondItem() || itemstack1.count >= merchantrecipe.getBuyItem2().count) ? merchantrecipe : null;
} else {
for (int j = 0; j < this.size(); ++j) {
MerchantRecipe merchantrecipe1 = this.get(j);
if (this.a(itemstack, merchantrecipe1.getBuyItem1()) && itemstack.count >= merchantrecipe1.getBuyItem1().count && (!merchantrecipe1.hasSecondItem() && itemstack1 == null || merchantrecipe1.hasSecondItem() && this.a(itemstack1, merchantrecipe1.getBuyItem2()) && itemstack1.count >= merchantrecipe1.getBuyItem2().count)) {
return merchantrecipe1;
}
}
return null;
}
}
private boolean a(ItemStack itemstack, ItemStack itemstack1) {
return ItemStack.c(itemstack, itemstack1) && (!itemstack1.hasTag() || itemstack.hasTag() && GameProfileSerializer.a(itemstack1.getTag(), itemstack.getTag(), false));
}
public void a(PacketDataSerializer packetdataserializer) {
packetdataserializer.writeByte((byte) (this.size() & 255));
for (int i = 0; i < this.size(); ++i) {
MerchantRecipe merchantrecipe = this.get(i);
packetdataserializer.a(merchantrecipe.getBuyItem1());
packetdataserializer.a(merchantrecipe.getBuyItem3());
ItemStack itemstack = merchantrecipe.getBuyItem2();
packetdataserializer.writeBoolean(itemstack != null);
if (itemstack != null) {
packetdataserializer.a(itemstack);
}
packetdataserializer.writeBoolean(merchantrecipe.h());
packetdataserializer.writeInt(merchantrecipe.e());
packetdataserializer.writeInt(merchantrecipe.f());
}
}
public void a(NBTTagCompound nbttagcompound) {
NBTTagList nbttaglist = nbttagcompound.getList("Recipes", 10);
for (int i = 0; i < nbttaglist.size(); ++i) {
NBTTagCompound nbttagcompound1 = nbttaglist.get(i);
this.add(new MerchantRecipe(nbttagcompound1));
}
}
public NBTTagCompound a() {
NBTTagCompound nbttagcompound = new NBTTagCompound();
NBTTagList nbttaglist = new NBTTagList();
for (int i = 0; i < this.size(); ++i) {
MerchantRecipe merchantrecipe = (MerchantRecipe) this.get(i);
if (merchantrecipe.getBuyItem1() == null) continue;
nbttaglist.add(merchantrecipe.k());
}
nbttagcompound.set("Recipes", nbttaglist);
return nbttagcompound;
}
}

View File

@ -37,6 +37,7 @@ import java.util.List;
import java.util.Queue;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
// CraftBukkit end
@ -64,8 +65,8 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
public final RollingAverage tps15 = new RollingAverage(60 * 15);
protected final ICommandHandler b;
protected final Proxy e;
protected final Queue<FutureTask<?>> j = new java.util.concurrent.ConcurrentLinkedQueue<>(); // Spigot, PAIL: Rename
protected final Queue<FutureTask<?>> fastPackets = new java.util.concurrent.ConcurrentLinkedQueue<>(); // eSpigot
protected final Deque<FutureTask<?>> j = new ConcurrentLinkedDeque<>(); // Spigot, PAIL: Rename // Paper - Make size() constant-time
public final Deque<FutureTask<?>> taskQueue = j; // SportBukkit - alias and downcast
private final MojangStatisticsGenerator n = new MojangStatisticsGenerator("server", this, az());
private final List<IUpdatePlayerListBox> p = Lists.newArrayList();
private final ServerPing r = new ServerPing();
@ -89,7 +90,7 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
public org.bukkit.command.ConsoleCommandSender console;
public org.bukkit.command.RemoteConsoleCommandSender remoteConsole;
public ConsoleReader reader;
public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<>();
//public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>(); SportBukkit - use Mojang's task queue
public int autosavePeriod;
public double[] recentTps = new double[3]; // PaperSpigot - Fine have your darn compat with bad plugins
@ -600,6 +601,7 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
this.g = 0;
this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.POSTWORLD); // CraftBukkit
LoginListener.allowLogins(); // Paper - Allow logins once postworld
}
protected void saveChunks(boolean flag) throws ExceptionWorldConflict { // CraftBukkit - added throws
@ -677,7 +679,7 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
// Spigot start
if (org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly) {
LOGGER.info("Saving usercache.json");
this.Z.c();
this.Z.c(false); // Paper
}
//Spigot end
@ -939,15 +941,7 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
public void B() {
SpigotTimings.minecraftSchedulerTimer.startTiming(); // Spigot
this.methodProfiler.a("jobs");
Queue queue = this.j;
// Spigot start
FutureTask<?> entry;
int count = this.j.size();
while (count-- > 0 && (entry = this.j.poll()) != null) {
SystemUtils.a(entry, MinecraftServer.LOGGER);
}
// Spigot end
processTasks();
SpigotTimings.minecraftSchedulerTimer.stopTiming(); // Spigot
this.methodProfiler.c("levels");
@ -957,13 +951,6 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
this.server.getScheduler().mainThreadHeartbeat(this.ticks);
SpigotTimings.bukkitSchedulerTimer.stopTiming(); // Spigot
// Run tasks that are waiting on processing
SpigotTimings.processQueueTimer.startTiming(); // Spigot
while (!processQueue.isEmpty()) {
processQueue.remove().run();
}
SpigotTimings.processQueueTimer.stopTiming(); // Spigot
SpigotTimings.chunkIOTickTimer.startTiming(); // Spigot
org.bukkit.craftbukkit.chunkio.ChunkIOExecutor.tick();
SpigotTimings.chunkIOTickTimer.stopTiming(); // Spigot
@ -988,6 +975,7 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
// if (i == 0 || this.getAllowNether()) {
WorldServer worldserver = this.worlds.get(i);
if(!worldserver.getWorld().checkTicking()) continue; // SportBukkit
this.methodProfiler.a(worldserver.getWorldData().getName());
/* Drop global time updates
@ -1654,6 +1642,48 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
return Thread.currentThread() == this.serverThread;
}
// SportBukkit start
public void addMainThreadTask(FutureTask<?> task) {
addMainThreadTask(false, task);
}
public void addMainThreadTask(boolean priority, FutureTask<?> task) {
synchronized(taskQueue) {
if(priority) {
//taskQueue.
taskQueue.addFirst(task);
} else {
taskQueue.addLast(task);
}
}
}
public void addMainThreadTask(Runnable task) {
addMainThreadTask(false, task);
}
public <T> ListenableFuture<T> addMainThreadTask(boolean priority, Runnable task) {
final ListenableFutureTask<T> future = ListenableFutureTask.create(task, null);
addMainThreadTask(priority, future);
return future;
}
protected void processTasks() {
if(!isMainThread()) throw new IllegalStateException("Tasks must be processed on the main thread");
for(;;) {
final FutureTask<?> task;
synchronized(taskQueue) {
task = taskQueue.poll();
}
if(task == null) {
break;
} else {
SystemUtils.a(task, MinecraftServer.LOGGER);
}
}
}
// SportBukkit end
public int aK() {
return 256;
}

View File

@ -12,7 +12,7 @@ import org.apache.logging.log4j.Logger;
public class NBTTagList extends NBTBase {
private static final Logger b = LogManager.getLogger();
private List<NBTBase> list = Lists.newArrayList();
public List<NBTBase> list = Lists.newArrayList(); // Paper
private byte type = 0;
public NBTTagList() {}

View File

@ -5,7 +5,7 @@ import java.util.List;
public abstract class NavigationAbstract {
protected EntityInsentient b;
protected EntityInsentient b; public Entity getEntity() { return b; } // Paper - OBFHELPER
protected World c;
protected PathEntity d;
protected double e;
@ -38,6 +38,7 @@ public abstract class NavigationAbstract {
}
public PathEntity a(BlockPosition blockposition) {
if (!getEntity().getWorld().getWorldBorder().isInBounds(blockposition)) return null; // Paper - don't path out of world border
if (!this.b()) {
return null;
} else {
@ -72,6 +73,7 @@ public abstract class NavigationAbstract {
this.c.methodProfiler.a("pathfind");
BlockPosition blockposition = (new BlockPosition(this.b)).up();
if (!getEntity().getWorld().getWorldBorder().isInBounds(blockposition)) return null; // Paper - don't path out of world border
int i = (int) (f + 16.0F);
ChunkCache chunkcache = new ChunkCache(this.c, blockposition.a(-i, -i, -i), blockposition.a(i, i, i), 0);
PathEntity pathentity = this.j.a(chunkcache, this.b, entity, f);

View File

@ -81,33 +81,18 @@ public class PacketPlayOutSpawnEntity implements Packet<PacketListenerPlayOut> {
this.d = packetdataserializer.readInt();
this.h = packetdataserializer.readByte();
this.i = packetdataserializer.readByte();
this.k = packetdataserializer.readInt();
if (this.k > 0) {
this.e = packetdataserializer.readShort();
this.f = packetdataserializer.readShort();
this.g = packetdataserializer.readShort();
}
}
public void b(PacketDataSerializer packetdataserializer) throws IOException {
packetdataserializer.b(this.a);
packetdataserializer.writeByte(this.j);
switch (this.k) {
case 0: {
this.d -= 32;
this.i = 128;
break;
}
case 1: {
this.b += 32;
this.i = 64;
break;
}
case 2: {
this.d += 32;
this.i = 0;
break;
}
case 3: {
this.b -= 32;
this.i = 192;
break;
}
}
packetdataserializer.writeInt(this.b);
packetdataserializer.writeInt(this.c);
packetdataserializer.writeInt(this.d);
@ -119,6 +104,7 @@ public class PacketPlayOutSpawnEntity implements Packet<PacketListenerPlayOut> {
packetdataserializer.writeShort(this.f);
packetdataserializer.writeShort(this.g);
}
}
public void b_old(PacketDataSerializer packetdataserializer) throws IOException {

View File

@ -23,6 +23,7 @@ import org.apache.logging.log4j.Logger;
// CraftBukkit start
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.HashSet;
@ -79,11 +80,12 @@ public class PlayerConnection implements PacketListenerPlayIn, IUpdatePlayerList
private int g;
private boolean h;
private int i;
private long j;
private long k;
private long j; private void setLastPing(long lastPing) { this.j = lastPing;}; private long getLastPing() { return this.j;}; // Paper - OBFHELPER
private long k; private void setKeepAliveID(long keepAliveID) { this.k = keepAliveID;}; private long getKeepAliveID() {return this.k; }; // Paper - OBFHELPER
// CraftBukkit start - multithreaded fields
private volatile int chatThrottle;
private static final AtomicIntegerFieldUpdater chatSpamField = AtomicIntegerFieldUpdater.newUpdater(PlayerConnection.class, "chatThrottle");
private final AtomicInteger tabSpamLimiter = new java.util.concurrent.atomic.AtomicInteger(); // Paper - configurable tab spam limits
// CraftBukkit end
private int m;
private IntHashMap<Short> n = new IntHashMap();
@ -142,6 +144,7 @@ public class PlayerConnection implements PacketListenerPlayIn, IUpdatePlayerList
this.minecraftServer.methodProfiler.b();
// CraftBukkit start
for (int spam; (spam = this.chatThrottle) > 0 && !chatSpamField.compareAndSet(this, spam, spam - 1); ) ;
if (tabSpamLimiter.get() > 0) tabSpamLimiter.getAndDecrement(); // Paper - split to seperate variable
/* Use thread-safe field access instead
if (this.chatThrottle > 0) {
--this.chatThrottle;
@ -648,6 +651,8 @@ public class PlayerConnection implements PacketListenerPlayIn, IUpdatePlayerList
double d3 = d0 * d0 + d1 * d1 + d2 * d2;
if (d3 > 36.0D) {
if (worldserver.isChunkLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4, true)) // Paper - Fix block break desync - Don't send for unloaded chunks
this.sendPacket(new PacketPlayOutBlockChange(worldserver, blockposition)); // Paper - Fix block break desync
return;
} else if (blockposition.getY() >= this.minecraftServer.getMaxBuildHeight()) {
return;
@ -761,7 +766,24 @@ public class PlayerConnection implements PacketListenerPlayIn, IUpdatePlayerList
}
}
if (!cancelled) {
if (cancelled) {
this.player.getBukkitEntity().updateInventory(); // SPIGOT-2524
// SportPaper start - Fix client desync
if (itemstack.getItem() == Item.getItemOf(Blocks.WATERLILY)) {
MovingObjectPosition movingObjectPosition1 = this.player.world.rayTrace(vec3d, vec3d1, true, false, false);
if (movingObjectPosition1 != null) {
BlockPosition blockPosition = movingObjectPosition1.a().up();
org.bukkit.craftbukkit.block.CraftBlockState.getBlockState(worldserver, blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()).update(true, false);
}
} else if (itemstack.getItem() == Items.BUCKET) {
MovingObjectPosition movingObjectPosition1 = this.player.world.rayTrace(vec3d, vec3d1, true, false, false);
if (movingObjectPosition1 != null) {
BlockPosition blockPosition = movingObjectPosition1.a();
org.bukkit.craftbukkit.block.CraftBlockState.getBlockState(worldserver, blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()).update(true, false);
}
}
// SportPaper end
} else {
this.player.playerInteractManager.useItem(this.player, this.player.world, itemstack);
}
}
@ -1019,7 +1041,7 @@ public class PlayerConnection implements PacketListenerPlayIn, IUpdatePlayerList
}
};
this.minecraftServer.processQueue.add(waitable);
this.minecraftServer.addMainThreadTask(waitable);
try {
waitable.get();
@ -1049,7 +1071,7 @@ public class PlayerConnection implements PacketListenerPlayIn, IUpdatePlayerList
} else if (getPlayer().isConversing()) {
// Spigot start
final String message = s;
this.minecraftServer.processQueue.add( new Waitable()
this.minecraftServer.addMainThreadTask( new Waitable()
{
@Override
protected Object evaluate()
@ -1096,7 +1118,7 @@ public class PlayerConnection implements PacketListenerPlayIn, IUpdatePlayerList
}
};
this.minecraftServer.processQueue.add(waitable);
this.minecraftServer.addMainThreadTask(waitable);
try {
waitable.get();
@ -1133,7 +1155,7 @@ public class PlayerConnection implements PacketListenerPlayIn, IUpdatePlayerList
return null;
}
};
minecraftServer.processQueue.add(wait);
minecraftServer.addMainThreadTask(wait);
try {
wait.get();
return;
@ -1179,7 +1201,7 @@ public class PlayerConnection implements PacketListenerPlayIn, IUpdatePlayerList
return null;
}};
if (async) {
minecraftServer.processQueue.add(waitable);
minecraftServer.addMainThreadTask(waitable);
} else {
waitable.run();
}
@ -1435,6 +1457,7 @@ public class PlayerConnection implements PacketListenerPlayIn, IUpdatePlayerList
}
if (event.isCancelled()) {
this.player.updateInventory(this.player.activeContainer); // Paper - Refresh player inventory
return;
}
// CraftBukkit end
@ -1565,7 +1588,7 @@ public class PlayerConnection implements PacketListenerPlayIn, IUpdatePlayerList
}
if (packetplayinwindowclick.c() == 0 || packetplayinwindowclick.c() == 1) {
action = InventoryAction.NOTHING; // Don't want to repeat ourselves
if (packetplayinwindowclick.b() == -999) {
if (packetplayinwindowclick.b() < 0) { // Paper - GH-404
if (player.inventory.getCarried() != null) {
action = packetplayinwindowclick.c() == 0 ? InventoryAction.DROP_ALL_CURSOR : InventoryAction.DROP_ONE_CURSOR;
}
@ -2006,6 +2029,7 @@ public class PlayerConnection implements PacketListenerPlayIn, IUpdatePlayerList
}
private long getCurrentMillis() { return d(); } // Paper - OBFHELPER
private long d() {
return System.nanoTime() / 1000000L;
}
@ -2028,7 +2052,7 @@ public class PlayerConnection implements PacketListenerPlayIn, IUpdatePlayerList
public void a(PacketPlayInTabComplete packetplayintabcomplete) {
PlayerConnectionUtils.ensureMainThread(packetplayintabcomplete, this, this.player.u());
// CraftBukkit start
if (chatSpamField.addAndGet(this, 10) > 500 && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) {
if (tabSpamLimiter.addAndGet(PaperSpigotConfig.tabSpamIncrement) > PaperSpigotConfig.tabSpamLimit && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) { // Paper start - split and make configurable
this.disconnect("disconnect.spam");
return;
}

View File

@ -157,6 +157,7 @@ public abstract class PlayerList {
playerconnection.sendPacket(new PacketPlayOutSpawnPosition(blockposition));
playerconnection.sendPacket(new PacketPlayOutAbilities(entityplayer.abilities));
playerconnection.sendPacket(new PacketPlayOutHeldItemSlot(entityplayer.inventory.itemInHandIndex));
playerconnection.sendPacket(new PacketPlayOutEntityStatus(entityplayer, (byte) (worldserver.getGameRules().getBoolean("reducedDebugInfo") ? 22 : 23))); // Paper - fix this rule not being initialized on the client
entityplayer.getStatisticManager().d();
entityplayer.getStatisticManager().updateStatistics(entityplayer);
this.sendScoreboard((ScoreboardServer) worldserver.getScoreboard(), entityplayer);
@ -561,6 +562,7 @@ public abstract class PlayerList {
BlockPosition blockposition1;
// CraftBukkit start - fire PlayerRespawnEvent
this.players.add(entityplayer); // Add player back to this list earlier than vanilla does
if (location == null) {
boolean isBedSpawn = false;
CraftWorld cworld = (CraftWorld) this.server.server.getWorld(entityplayer.spawnWorld);
@ -622,10 +624,12 @@ public abstract class PlayerList {
entityplayer.playerConnection.sendPacket(new PacketPlayOutExperience(entityplayer.exp, entityplayer.expTotal, entityplayer.expLevel));
this.b(entityplayer, worldserver);
if (!entityplayer.playerConnection.isDisconnected()) {
// Don't re-add player to player list if disconnected
if (entityplayer.playerConnection.isDisconnected()) {
this.players.remove(entityplayer);
} else {
worldserver.getPlayerChunkMap().addPlayer(entityplayer);
worldserver.addEntity(entityplayer);
this.players.add(entityplayer);
this.playersByName.put(entityplayer.getName(), entityplayer); // Spigot
this.j.put(entityplayer.getUniqueID(), entityplayer);
}

View File

@ -1,16 +1,19 @@
package net.minecraft.server;
import com.google.common.collect.Maps;
import org.github.paperspigot.PaperSpigotConfig;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
public class RegionFileCache {
public static final Map<File, RegionFile> a = Maps.newHashMap(); // Spigot - private -> public
public static final Map<File, RegionFile> a = new LinkedHashMap(PaperSpigotConfig.regionFileCacheSize, 0.75f, true); // Spigot - private -> public, Paper - HashMap -> LinkedHashMap
// PaperSpigot start
public static synchronized RegionFile a(File file, int i, int j) {
@ -30,8 +33,8 @@ public class RegionFileCache {
file1.mkdirs();
}
if (RegionFileCache.a.size() >= 256) {
a();
if (RegionFileCache.a.size() >= PaperSpigotConfig.regionFileCacheSize) { // Paper
trimCache(); // Paper
}
RegionFile regionfile1 = new RegionFile(file2);
@ -41,6 +44,21 @@ public class RegionFileCache {
}
}
// Paper Start
private static synchronized void trimCache() {
Iterator<Map.Entry<File, RegionFile>> itr = RegionFileCache.a.entrySet().iterator();
int count = RegionFileCache.a.size() - PaperSpigotConfig.regionFileCacheSize;
while (count-- >= 0 && itr.hasNext()) {
try {
itr.next().getValue().c();
} catch (IOException ioexception) {
ioexception.printStackTrace();
}
itr.remove();
}
}
// Paper End
public static synchronized void a() {
for (RegionFile regionfile : RegionFileCache.a.values()) {

View File

@ -21,6 +21,7 @@ import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@ -69,7 +70,7 @@ public class ServerConnection {
this.d = true;
}
// KigPaper start - Paper-0117 by Aikar
// Paper start - prevent blocking on adding a new network manager while the server is ticking
private final List<NetworkManager> pending = Collections.synchronizedList(Lists.newArrayList());
private void addPending() {
synchronized (pending) {
@ -77,7 +78,7 @@ public class ServerConnection {
pending.clear();
}
}
// KigPaper end
// Paper end
public void a(InetAddress inetaddress, int i) throws IOException {
List list = this.g;
@ -115,7 +116,7 @@ public class ServerConnection {
NetworkManager networkmanager = new NetworkManager(EnumProtocolDirection.SERVERBOUND);
//ServerConnection.this.h.add(networkmanager);
pending.add(networkmanager); // KigPaper
pending.add(networkmanager); // Paper
channel.pipeline().addLast("packet_handler", networkmanager);
networkmanager.a(new HandshakeListener(ServerConnection.this.f, networkmanager));
}
@ -126,14 +127,26 @@ public class ServerConnection {
public void b() {
this.d = false;
// CraftBukkit start - handle processQueue while closing channels to prevent deadlock
ArrayList<ChannelFuture> futures = new ArrayList<ChannelFuture>();
for (ChannelFuture channelfuture : this.g) {
try {
channelfuture.channel().close().sync();
} catch (InterruptedException interruptedexception) {
ServerConnection.e.error("Interrupted whilst closing channel");
}
futures.add(channelfuture.channel().close());
}
for(;;) {
futures.removeIf(java.util.concurrent.Future::isDone);
f.processTasks();
if(futures.isEmpty()) break;
try {
Thread.sleep(50);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
// CraftBukkit end
}
public void processFastPackets() {

View File

@ -46,6 +46,7 @@ public final class SpawnerCreature {
// Spigot end
public int a(WorldServer worldserver, boolean flag, boolean flag1, boolean flag2) {
org.spigotmc.AsyncCatcher.catchOp("check for eligible spawn chunks"); // Paper - At least until we figure out what is calling this async
if (!flag && !flag1) {
return 0;
} else {
@ -122,8 +123,10 @@ public final class SpawnerCreature {
// CraftBukkit end
if ((!enumcreaturetype.d() || flag1) && (enumcreaturetype.d() || flag) && (!enumcreaturetype.e() || flag2)) {
/* Paper start - As far as I can tell neither of these are even used
k = worldserver.a(enumcreaturetype.a());
int l1 = limit * i / a; // CraftBukkit - use per-world limits
*/ // Paper end
if ((mobcnt = getEntityCount(worldserver, enumcreaturetype.a())) <= limit * i / 289) { // TacoSpigot - use 17x17 like vanilla (a at top of file)
Iterator iterator1 = this.b.iterator();

View File

@ -188,7 +188,7 @@ public class TileEntityFurnace extends TileEntityContainer implements IUpdatePla
if (this.isBurning() && this.canBurn()) {
this.cookTime += elapsedTicks;
if (this.cookTime >= this.cookTimeTotal) {
this.cookTime = 0;
this.cookTime -= this.cookTimeTotal; // Paper
this.cookTimeTotal = this.a(this.items[0]);
this.burn();
flag1 = true;

View File

@ -164,7 +164,7 @@ public class TileEntitySkull extends TileEntity {
} else {
executor.execute(() -> {
final GameProfile profile1 = skinCache.getUnchecked(gameprofile.getName().toLowerCase());
MinecraftServer.getServer().processQueue.add(() -> {
MinecraftServer.getServer().addMainThreadTask(() -> {
if (profile1 == null) {
callback.apply(gameprofile);
} else {

View File

@ -35,6 +35,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
public class UserCache {
@ -82,7 +83,7 @@ public class UserCache {
};
minecraftserver.getGameProfileRepository().findProfilesByNames(new String[] { s}, Agent.MINECRAFT, profilelookupcallback);
if (!minecraftserver.getOnlineMode() && agameprofile[0] == null) {
if (!minecraftserver.getOnlineMode() && agameprofile[0] == null && !StringUtils.isBlank(s)) { // Paper - Don't lookup a profile with a blank
UUID uuid = EntityHuman.a(new GameProfile(null, s));
GameProfile gameprofile = new GameProfile(uuid, s);
@ -96,7 +97,7 @@ public class UserCache {
this.a(gameprofile, null);
}
private void a(GameProfile gameprofile, Date date) {
private synchronized void a(GameProfile gameprofile, Date date) { // Paper - synchronize
UUID uuid = gameprofile.getId();
if (date == null) {
@ -110,8 +111,9 @@ public class UserCache {
String s = gameprofile.getName().toLowerCase(Locale.ROOT);
UserCache.UserCacheEntry usercache_usercacheentry = new UserCache.UserCacheEntry(gameprofile, date, null);
if (this.d.containsKey(uuid)) {
//if (this.d.containsKey(uuid)) { // Paper
UserCache.UserCacheEntry usercache_usercacheentry1 = this.d.get(uuid);
if (usercache_usercacheentry1 != null) { // Paper
this.c.remove(usercache_usercacheentry1.a().getName().toLowerCase(Locale.ROOT));
this.e.remove(gameprofile);
@ -123,7 +125,7 @@ public class UserCache {
if( !org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly ) this.c(); // Spigot - skip saving if disabled
}
public GameProfile getProfile(String s) {
public synchronized GameProfile getProfile(String s) { // Paper - synchronize
String s1 = s.toLowerCase(Locale.ROOT);
UserCache.UserCacheEntry usercache_usercacheentry = this.c.get(s1);
@ -152,13 +154,14 @@ public class UserCache {
return usercache_usercacheentry == null ? null : usercache_usercacheentry.a();
}
public String[] a() {
public synchronized String[] a() { // Paper - synchronize
ArrayList arraylist = Lists.newArrayList(this.c.keySet());
return (String[]) arraylist.toArray(new String[0]);
}
public GameProfile a(UUID uuid) {
public GameProfile getProfile(UUID uuid) { return a(uuid); } // Paper - OBFHELPER
public synchronized GameProfile a(UUID uuid) { // Paper - synchronize
UserCache.UserCacheEntry usercache_usercacheentry = this.d.get(uuid);
return usercache_usercacheentry == null ? null : usercache_usercacheentry.a();
@ -195,21 +198,23 @@ public class UserCache {
this.a(usercache_usercacheentry.a(), usercache_usercacheentry.b());
}
}
} catch (FileNotFoundException filenotfoundexception) {
// Spigot Start
} catch (com.google.gson.JsonSyntaxException ex) {
} catch (Exception ex) {
// SportPaper - Catch all UserCache exceptions in one and always delete
JsonList.a.warn( "Usercache.json is corrupted or has bad formatting. Deleting it to prevent further issues." );
this.g.delete();
// Spigot End
} catch (JsonParseException ignored) {
} finally {
IOUtils.closeQuietly(bufferedreader);
}
}
// Paper start
public void c() {
c(true);
}
public void c(boolean asyncSave) {
String s = this.b.toJson(this.a(org.spigotmc.SpigotConfig.userCacheCap));
Runnable save = () -> {
BufferedWriter bufferedwriter = null;
try {
@ -218,10 +223,18 @@ public class UserCache {
return;
} catch (FileNotFoundException filenotfoundexception) {
return;
} catch (IOException ignored) {
} catch (IOException ioexception) {
;
} finally {
IOUtils.closeQuietly(bufferedwriter);
}
};
if (asyncSave) {
MCUtil.scheduleAsyncTask(save);
} else {
save.run();
}
// Paper end
}

View File

@ -24,6 +24,8 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.bukkit.util.Vector;
import javax.annotation.Nullable;
// PaperSpigot end
// CraftBukkit start
@ -438,7 +440,7 @@ public abstract class World implements IBlockAccess {
// CraftBukkit start - capture blockstates
BlockState blockstate = null;
if (this.captureBlockStates) {
blockstate = org.bukkit.craftbukkit.block.CraftBlockState.getBlockState(this, blockposition.getX(), blockposition.getY(), blockposition.getZ(), i);
blockstate = world.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()).getState(); // Paper - use CB getState to get a suitable snapshot
this.capturedBlockStates.add(blockstate);
}
// CraftBukkit end
@ -488,7 +490,7 @@ public abstract class World implements IBlockAccess {
// CraftBukkit start - Split off from original setTypeAndData(int i, int j, int k, Block block, int l, int i1) method in order to directly send client and physic updates
public void notifyAndUpdatePhysics(BlockPosition blockposition, Chunk chunk, Block oldBlock, Block newBLock, int flag) {
if ((flag & 2) != 0 && (chunk == null || chunk.isReady())) { // allow chunk to be null here as chunk.isReady() is false when we send our notification during block placement
if ((flag & 2) != 0 && (!this.isClientSide || (flag & 4) == 0) && (chunk == null || chunk.isReady())) { // allow chunk to be null here as chunk.isReady() is false when we send our notification during block placement
this.notify(blockposition);
}
@ -769,6 +771,7 @@ public abstract class World implements IBlockAccess {
if (blockposition.getY() >= 256) {
blockposition = new BlockPosition(blockposition.getX(), 255, blockposition.getZ());
}
if (!this.isLoaded(blockposition)) return 0; // Paper
Chunk chunk = this.getChunkAtWorldCoords(blockposition);
@ -921,7 +924,8 @@ public abstract class World implements IBlockAccess {
int i1 = MathHelper.floor(vec3d.b);
int j1 = MathHelper.floor(vec3d.c);
BlockPosition blockposition = new BlockPosition(l, i1, j1);
IBlockData iblockdata = this.getType(blockposition);
IBlockData iblockdata = this.getTypeIfLoaded(blockposition); // SportPaper
if (iblockdata == null) return null; // SportPaper
Block block = iblockdata.getBlock();
if ((!flag1 || block.a(this, blockposition, iblockdata) != null) && block.a(iblockdata, flag)) {
@ -1023,7 +1027,8 @@ public abstract class World implements IBlockAccess {
i1 = MathHelper.floor(vec3d.b) - (enumdirection == EnumDirection.UP ? 1 : 0);
j1 = MathHelper.floor(vec3d.c) - (enumdirection == EnumDirection.SOUTH ? 1 : 0);
blockposition = new BlockPosition(l, i1, j1);
IBlockData iblockdata1 = this.getType(blockposition);
IBlockData iblockdata1 = this.getTypeIfLoaded(blockposition); // SportPaper
if (iblockdata1 == null) return null; // SportPaper
Block block1 = iblockdata1.getBlock();
if (!flag1 || block1.a(this, blockposition, iblockdata1) != null) {
@ -1118,6 +1123,18 @@ public abstract class World implements IBlockAccess {
public boolean addEntity(Entity entity, SpawnReason spawnReason) { // Changed signature, added SpawnReason
org.spigotmc.AsyncCatcher.catchOp( "entity add"); // Spigot
if (entity == null) return false;
// Workaround for https://bugs.mojang.com/browse/MC-72248
// If an EntityFallingBlock spawns inside a block of the same type, the client will ALWAYS remove the block,
// whereas the server will only remove it if ticksLived is 0. This creates invisible blocks on the client.
// The imperfect workaround is to not spawn the falling block at all if it will cause such a desync.
if(entity instanceof EntityFallingBlock && ((EntityFallingBlock) entity).getBlock().getBlock() == this.getType(new BlockPosition(entity)).getBlock()) {
EntityFallingBlock fallingBlock = (EntityFallingBlock) entity;
if(fallingBlock.ticksLived != 0 && fallingBlock.getBlock().getBlock() == this.getType(new BlockPosition(fallingBlock)).getBlock()) {
return false;
}
}
// CraftBukkit end
int i = MathHelper.floor(entity.locX / 16.0D);
int j = MathHelper.floor(entity.locZ / 16.0D);
@ -1372,6 +1389,7 @@ public abstract class World implements IBlockAccess {
// Spigot end
if (entity instanceof EntityItem) return arraylist; // PaperSpigot - Optimize item movement
if (entity instanceof EntityArmorStand && !entity.world.paperSpigotConfig.armorStandEntityLookups) return arraylist; // Paper
if (entity instanceof EntityArmorStand) return arraylist; // TacoSpigot - Optimize armor stand movement
if (entity instanceof EntityTNTPrimed) return arraylist; // TacoSpigot - Optimize tnt entity movement
if (entity instanceof EntityFallingBlock) return arraylist; // TacoSpigot - Optimize falling block movement
@ -1892,7 +1910,7 @@ public abstract class World implements IBlockAccess {
}
int k = MathHelper.floor(entity.locX / 16.0D);
int l = MathHelper.floor(entity.locY / 16.0D);
int l = Math.min(15, Math.max(0, MathHelper.floor(entity.locY / 16.0D))); // Paper - stay consistent with chunk add/remove behavior
int i1 = MathHelper.floor(entity.locZ / 16.0D);
if (!entity.ad || entity.ae != k || entity.af != l || entity.ag != i1) {
@ -1926,6 +1944,33 @@ public abstract class World implements IBlockAccess {
return this.a(axisalignedbb, (Entity) null);
}
// Paper start - Based on method below
/**
* @param axisalignedbb area to search within
* @param entity causing the action ex. block placer
* @return if there are no visible players colliding
*/
public boolean checkNoVisiblePlayerCollisions(AxisAlignedBB axisalignedbb, @Nullable Entity entity) {
List list = this.getEntities((Entity) null, axisalignedbb);
for (int i = 0; i < list.size(); ++i) {
Entity entity1 = (Entity) list.get(i);
if (entity instanceof EntityPlayer && entity1 instanceof EntityPlayer) {
if (!((EntityPlayer) entity).getBukkitEntity().canSee(((EntityPlayer) entity1).getBukkitEntity())) {
continue;
}
}
if (!entity1.dead && entity1.blocksEntitySpawning()) {
return false;
}
}
return true;
}
// Paper end
public boolean a(AxisAlignedBB axisalignedbb, Entity entity) {
List list = this.getEntities(null, axisalignedbb);
@ -2115,6 +2160,35 @@ public abstract class World implements IBlockAccess {
}
// ImHacking end
// CraftBukkit start
public Vec3D getLargestBlockIntersection(AxisAlignedBB aabb, Material material) {
int xMin = MathHelper.floor(aabb.a);
int xMax = MathHelper.floor(aabb.d + 1.0D);
int yMin = MathHelper.floor(aabb.b);
int yMax = MathHelper.floor(aabb.e + 1.0D);
int zMin = MathHelper.floor(aabb.c);
int zMax = MathHelper.floor(aabb.f + 1.0D);
double maxVolume = 0;
Vec3D block = null;
for (int x = xMin; x < xMax; ++x) {
for (int y = yMin; y < yMax; ++y) {
for (int z = zMin; z < zMax; ++z) {
IBlockData type = this.getType(new BlockPosition(x, y, z));
if (material == type.getBlock().getMaterial()) {
AxisAlignedBB i = new AxisAlignedBB(x, x+1, y, y+1, z, z+1).a(aabb);
double volume = (i.d - i.a) * (i.e - i.b) * (i.f - i.c);
if(volume > maxVolume) {
maxVolume = volume;
block = new Vec3D(x, y, z);
}
}
}
}
}
return block;
}
// CraftBukkit end
public boolean b(AxisAlignedBB axisalignedbb, Material material) {
int i = MathHelper.floor(axisalignedbb.a);
int j = MathHelper.floor(axisalignedbb.d + 1.0D);
@ -2991,7 +3065,7 @@ public abstract class World implements IBlockAccess {
AxisAlignedBB axisalignedbb = flag ? null : block.a(this, blockposition, block.getBlockData());
// CraftBukkit start - store default return
boolean defaultReturn = (axisalignedbb == null || this.a(axisalignedbb, entity)) && (block1.getMaterial() == Material.ORIENTABLE && block == Blocks.ANVIL || block1.getMaterial().isReplaceable() && block.canPlace(this, blockposition, enumdirection, itemstack));
boolean defaultReturn = axisalignedbb != null && !this.checkNoVisiblePlayerCollisions(axisalignedbb, entity) ? false : (block1.getMaterial() == Material.ORIENTABLE && block == Blocks.ANVIL ? true : block1.getMaterial().isReplaceable() && block.canPlace(this, blockposition, enumdirection, itemstack)); // Paper - Use our entity search
BlockCanBuildEvent event = new BlockCanBuildEvent(this.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), CraftMagicNumbers.getId(block), defaultReturn);
this.getServer().getPluginManager().callEvent(event);
@ -3095,7 +3169,7 @@ public abstract class World implements IBlockAccess {
for (EntityHuman player : this.players) {
EntityHuman entityhuman1 = player;
// CraftBukkit start - Fixed an NPE
if (entityhuman1 == null || entityhuman1.dead) {
if (entityhuman1 == null || !entityhuman1.ad()) { // CraftBukkit allow for more complex logic by using the "is alive" method
continue;
}
// CraftBukkit end
@ -3141,7 +3215,7 @@ public abstract class World implements IBlockAccess {
for (EntityHuman player : this.players) {
EntityHuman entityhuman1 = player;
// CraftBukkit start - Fixed an NPE
if (entityhuman1 == null || entityhuman1.dead || !entityhuman1.affectsSpawning) {
if (entityhuman1 == null || !entityhuman1.ad() || !entityhuman1.affectsSpawning) { // CraftBukkit allow for more complex logic by using the "is alive" method
continue;
}
// CraftBukkit end

View File

@ -29,6 +29,7 @@ public class WorldBorder {
this.l = 5;
}
public boolean isInBounds(BlockPosition blockposition) { return a(blockposition); } // Paper - OBFHELPER
public boolean a(BlockPosition blockposition) {
return (double) (blockposition.getX() + 1) > this.b() && (double) blockposition.getX() < this.d() && (double) (blockposition.getZ() + 1) > this.c() && (double) blockposition.getZ() < this.e();
}

View File

@ -68,6 +68,7 @@ public class WorldManager implements IWorldAccess {
if (entity instanceof EntityHuman) entityhuman = (EntityHuman) entity;
// CraftBukkit end
PacketPlayOutBlockBreakAnimation packet = null; // SportPaper - cache packet
while (iterator.hasNext()) {
EntityPlayer entityplayer = (EntityPlayer) iterator.next();
@ -83,7 +84,10 @@ public class WorldManager implements IWorldAccess {
// CraftBukkit end
if (d0 * d0 + d1 * d1 + d2 * d2 < 1024.0D) {
entityplayer.playerConnection.sendPacket(new PacketPlayOutBlockBreakAnimation(i, blockposition, j));
// SportPaper start
if (packet == null) packet = new PacketPlayOutBlockBreakAnimation(i, blockposition, j);
entityplayer.playerConnection.sendPacket(packet);
// SportPaper end
}
}
}

View File

@ -959,15 +959,17 @@ public class WorldServer extends World implements IAsyncTaskHandler {
this.chunkProvider.saveChunks(flag, iprogressupdate);
// CraftBukkit - ArrayList -> Collection
Collection arraylist = this.chunkProviderServer.a();
/* //Paper start Collection arraylist = chunkproviderserver.a();
Iterator iterator = arraylist.iterator();
for (Object value : arraylist) {
Chunk chunk = (Chunk) value;
while (iterator.hasNext()) {
Chunk chunk = (Chunk) iterator.next();
if (chunk != null && !this.manager.a(chunk.locX, chunk.locZ)) {
this.chunkProviderServer.queueUnload(chunk.locX, chunk.locZ);
}
}
*/// Paper end
}
}

View File

@ -1,245 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache license, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the license for the specific language governing permissions and
* limitations under the license.
*/
package org.apache.logging.log4j.core.appender;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.nio.charset.Charset;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.helpers.Booleans;
import org.apache.logging.log4j.core.helpers.Loader;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.util.PropertiesUtil;
/**
* ConsoleAppender appends log events to <code>System.out</code> or
* <code>System.err</code> using a layout specified by the user. The
* default target is <code>System.out</code>.
* @doubt accessing System.out or .err as a byte stream instead of a writer
* bypasses the JVM's knowledge of the proper encoding. (RG) Encoding
* is handled within the Layout. Typically, a Layout will generate a String
* and then call getBytes which may use a configured encoding or the system
* default. OTOH, a Writer cannot print byte streams.
*/
@Plugin(name = "Console", category = "Core", elementType = "appender", printObject = true)
public final class ConsoleAppender extends AbstractOutputStreamAppender {
private static final String JANSI_CLASS = "org.fusesource.jansi.WindowsAnsiOutputStream";
private static ConsoleManagerFactory factory = new ConsoleManagerFactory();
/**
* Enumeration of console destinations.
*/
public enum Target {
/** Standard output. */
SYSTEM_OUT,
/** Standard error output. */
SYSTEM_ERR
}
private ConsoleAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
final OutputStreamManager manager,
final boolean ignoreExceptions) {
super(name, layout, filter, ignoreExceptions, true, manager);
}
/**
* Create a Console Appender.
* @param layout The layout to use (required).
* @param filter The Filter or null.
* @param t The target ("SYSTEM_OUT" or "SYSTEM_ERR"). The default is "SYSTEM_OUT".
* @param follow If true will follow changes to the underlying output stream.
* @param name The name of the Appender (required).
* @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
* they are propagated to the caller.
* @return The ConsoleAppender.
*/
@PluginFactory
public static ConsoleAppender createAppender(
@PluginElement("Layout") Layout<? extends Serializable> layout,
@PluginElement("Filters") final Filter filter,
@PluginAttribute("target") final String t,
@PluginAttribute("name") final String name,
@PluginAttribute("follow") final String follow,
@PluginAttribute("ignoreExceptions") final String ignore) {
if (name == null) {
LOGGER.error("No name provided for ConsoleAppender");
return null;
}
if (layout == null) {
layout = PatternLayout.createLayout(null, null, null, null, null);
}
final boolean isFollow = Boolean.parseBoolean(follow);
final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
final Target target = t == null ? Target.SYSTEM_OUT : Target.valueOf(t);
return new ConsoleAppender(name, layout, filter, getManager(isFollow, target, layout), ignoreExceptions);
}
private static OutputStreamManager getManager(final boolean follow, final Target target, final Layout<? extends Serializable> layout) {
final String type = target.name();
final OutputStream os = getOutputStream(follow, target);
return OutputStreamManager.getManager(target.name() + "." + follow, new FactoryData(os, type, layout), factory);
}
private static OutputStream getOutputStream(final boolean follow, final Target target) {
final String enc = Charset.defaultCharset().name();
PrintStream printStream = null;
try {
printStream = target == Target.SYSTEM_OUT ?
follow ? new PrintStream(new SystemOutStream(), true, enc) : System.out :
follow ? new PrintStream(new SystemErrStream(), true, enc) : System.err;
} catch (final UnsupportedEncodingException ex) { // should never happen
throw new IllegalStateException("Unsupported default encoding " + enc, ex);
}
final PropertiesUtil propsUtil = PropertiesUtil.getProperties();
if (!propsUtil.getStringProperty("os.name").startsWith("Windows") ||
propsUtil.getBooleanProperty("log4j.skipJansi")) {
return printStream;
}
try {
final ClassLoader loader = Loader.getClassLoader();
// We type the parameter as a wildcard to avoid a hard reference to Jansi.
final Class<?> clazz = loader.loadClass(JANSI_CLASS);
final Constructor<?> constructor = clazz.getConstructor(OutputStream.class);
return (OutputStream) constructor.newInstance(printStream);
} catch (final ClassNotFoundException cnfe) {
LOGGER.debug("Jansi is not installed, cannot find {}", JANSI_CLASS);
} catch (final NoSuchMethodException nsme) {
LOGGER.warn("{} is missing the proper constructor", JANSI_CLASS);
} catch (final Throwable ex) { // CraftBukkit - Exception -> Throwable
LOGGER.warn("Unable to instantiate {}", JANSI_CLASS);
}
return printStream;
}
/**
* An implementation of OutputStream that redirects to the current System.err.
*/
private static class SystemErrStream extends OutputStream {
public SystemErrStream() {
}
@Override
public void close() {
// do not close sys err!
}
@Override
public void flush() {
System.err.flush();
}
@Override
public void write(final byte[] b) throws IOException {
System.err.write(b);
}
@Override
public void write(final byte[] b, final int off, final int len)
throws IOException {
System.err.write(b, off, len);
}
@Override
public void write(final int b) {
System.err.write(b);
}
}
/**
* An implementation of OutputStream that redirects to the current System.out.
*/
private static class SystemOutStream extends OutputStream {
public SystemOutStream() {
}
@Override
public void close() {
// do not close sys out!
}
@Override
public void flush() {
System.out.flush();
}
@Override
public void write(final byte[] b) throws IOException {
System.out.write(b);
}
@Override
public void write(final byte[] b, final int off, final int len)
throws IOException {
System.out.write(b, off, len);
}
@Override
public void write(final int b) throws IOException {
System.out.write(b);
}
}
/**
* Data to pass to factory method.
*/
private static class FactoryData {
private final OutputStream os;
private final String type;
private final Layout<? extends Serializable> layout;
/**
* Constructor.
* @param os The OutputStream.
* @param type The name of the target.
* @param layout A Serializable layout
*/
public FactoryData(final OutputStream os, final String type, final Layout<? extends Serializable> layout) {
this.os = os;
this.type = type;
this.layout = layout;
}
}
/**
* Factory to create the Appender.
*/
private static class ConsoleManagerFactory implements ManagerFactory<OutputStreamManager, FactoryData> {
/**
* Create an OutputStreamManager.
* @param name The name of the entity to manage.
* @param data The data required to create the entity.
* @return The OutputStreamManager
*/
@Override
public OutputStreamManager createManager(final String name, final FactoryData data) {
return new OutputStreamManager(data.os, data.type, data.layout);
}
}
}

View File

@ -1,9 +1,7 @@
package org.bukkit.craftbukkit;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.*;
import net.minecraft.server.*;
@ -13,6 +11,7 @@ import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.craftbukkit.block.CraftBlock;
import org.bukkit.craftbukkit.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.entity.Entity;
import org.bukkit.ChunkSnapshot;
import org.bukkit.entity.HumanEntity;
@ -79,6 +78,29 @@ public class CraftChunk implements Chunk {
return new CraftBlock(this, (getX() << 4) | (x & 0xF), y, (getZ() << 4) | (z & 0xF));
}
@Override
public Set<Block> getBlocks(org.bukkit.Material material) {
Set<Block> blocks = new HashSet<Block>();
net.minecraft.server.Block nmsBlock = CraftMagicNumbers.getBlock(material);
net.minecraft.server.Chunk chunk = getHandle();
for(ChunkSection section : chunk.getSections()) {
if(section == null || section.a()) continue; // ChunkSection.a() -> true if section is empty
char[] blockIds = section.getIdArray();
for(int i = 0; i < blockIds.length; i++) {
// This does a lookup in the block registry, but does not create any objects, so should be pretty efficient
IBlockData blockData = net.minecraft.server.Block.d.a(blockIds[i]);
if(blockData != null && blockData.getBlock() == nmsBlock) {
blocks.add(getBlock(i & 0xf, section.getYPosition() | (i >> 8), (i >> 4) & 0xf));
}
}
}
return blocks;
}
@Override
public Entity[] getEntities() {
int count = 0, index = 0;

View File

@ -82,12 +82,7 @@ import org.bukkit.inventory.ShapedRecipe;
import org.bukkit.inventory.ShapelessRecipe;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.Permission;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginLoadOrder;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.ServicesManager;
import org.bukkit.plugin.SimplePluginManager;
import org.bukkit.plugin.SimpleServicesManager;
import org.bukkit.plugin.*;
import org.bukkit.plugin.java.JavaPluginLoader;
import org.bukkit.plugin.messaging.Messenger;
import org.bukkit.potion.Potion;
@ -649,7 +644,7 @@ public final class CraftServer implements Server {
return dispatchCommand(fSender, fCommandLine);
}
};
net.minecraft.server.MinecraftServer.getServer().processQueue.add(wait);
net.minecraft.server.MinecraftServer.getServer().addMainThreadTask(wait);
try {
return wait.get();
} catch (InterruptedException e) {
@ -731,8 +726,18 @@ public final class CraftServer implements Server {
world.tacoSpigotConfig.init(); // TacoSpigot
}
Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper
pluginManager.clearPlugins();
commandMap.clearCommands();
// Paper start
for (Plugin plugin : pluginClone) {
entityMetadata.removeAll(plugin);
worldMetadata.removeAll(plugin);
playerMetadata.removeAll(plugin);
}
// Paper end
resetRecipes();
org.spigotmc.SpigotConfig.registerCommands(); // Spigot
org.github.paperspigot.PaperSpigotConfig.registerCommands(); // PaperSpigot
@ -1787,6 +1792,46 @@ public final class CraftServer implements Server {
return CraftMagicNumbers.INSTANCE;
}
@Override
public void postToMainThread(Plugin plugin, boolean priority, Runnable task) {
getHandle().getServer().addMainThreadTask(priority, wrapTask(plugin, task));
}
@Override
public boolean runOnMainThread(Plugin plugin, boolean priority, Runnable task) {
task = wrapTask(plugin, task);
if(getHandle().getServer().isMainThread()) {
task.run();
return true;
} else {
postToMainThread(plugin, priority, task);
return false;
}
}
private Runnable wrapTask(final Plugin plugin, final Runnable task) {
if(!plugin.isEnabled()) {
throw new IllegalPluginAccessException("Plugin attempted to register task while disabled");
}
class Wrapped implements Runnable {
@Override public void run() {
try {
task.run();
} catch(Throwable throwable) {
plugin.getLogger().log(
Level.SEVERE,
"Exception running task from plugin " + plugin.getDescription().getFullName(),
throwable
);
}
}
}
return task instanceof Wrapped ? task : new Wrapped();
}
private final Spigot spigot = new Spigot()
{

View File

@ -55,6 +55,7 @@ import org.bukkit.metadata.MetadataValue;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.messaging.StandardMessenger;
import org.bukkit.util.Vector;
import org.github.paperspigot.PaperSpigotConfig;
public class CraftWorld implements World {
public static final int CUSTOM_DIMENSION_OFFSET = 10;
@ -76,6 +77,26 @@ public class CraftWorld implements World {
private int chunkLoadCount = 0;
private int chunkGCTickCount;
// Paper start - Provide fast information methods
public int getEntityCount() {
return world.entityList.size();
}
public int getTileEntityCount() {
// We don't use the full world tile entity list, so we must iterate chunks
int size = 0;
for (net.minecraft.server.Chunk chunk : world.chunkProviderServer.chunks.values()) {
size += chunk.tileEntities.size();
}
return size;
}
public int getChunkCount() {
return world.chunkProviderServer.chunks.size();
}
public int getPlayerCount() {
return world.players.size();
}
// Paper end
private static final Random rand = new Random();
public CraftWorld(WorldServer world, ChunkGenerator gen, Environment env) {
@ -89,6 +110,32 @@ public class CraftWorld implements World {
}
}
private boolean ticking = false;
public boolean isTicking() {
return ticking;
}
public boolean checkTicking() {
boolean shouldTick = PaperSpigotConfig.tickEmptyWorlds || hasPlayers();
if(ticking) {
if(!shouldTick) { // Empty
ticking = false;
world.getServer().getLogger().info("Stopping world " + getName());
getHandle().chunkProviderServer.unloadAllChunks();
}
} else if(shouldTick) {
ticking = true;
world.getServer().getLogger().info("Starting world " + getName());
setKeepSpawnInMemory(getKeepSpawnInMemory());
}
return ticking;
}
public boolean hasPlayers() {
return getPlayerCount() > 0;
}
public Block getBlockAt(int x, int y, int z) {
return getChunkAt(x >> 4, z >> 4).getBlock(x & 0xF, y, z & 0xF);
}
@ -299,7 +346,6 @@ public class CraftWorld implements World {
return world.chunkProviderServer.getChunkAt(x, z) != null;
}
world.chunkProviderServer.unloadQueue.remove(LongHash.toLong(x, z)); // TacoSpigot - invoke LongHash directly
net.minecraft.server.Chunk chunk = world.chunkProviderServer.chunks.get(LongHash.toLong(x, z));
if (chunk == null) {
@ -309,6 +355,7 @@ public class CraftWorld implements World {
chunkLoadPostProcess(chunk, x, z);
world.timings.syncChunkLoadTimer.stopTiming(); // Spigot
}
world.chunkProviderServer.unloadQueue.remove(LongHash.toLong(x, z));
return chunk != null;
}
@ -906,7 +953,7 @@ public class CraftWorld implements World {
Validate.isTrue(material.isBlock(), "Material must be a block");
double x = location.getBlockX() + 0.5;
double y = location.getBlockY() + 0.5;
double y = location.getBlockY();
double z = location.getBlockZ() + 0.5;
// PaperSpigot start - Add FallingBlock source location API
@ -940,6 +987,9 @@ public class CraftWorld implements World {
// order is important for some of these
if (Boat.class.isAssignableFrom(clazz)) {
entity = new EntityBoat(world, x, y, z);
} else if (org.bukkit.entity.Item.class.isAssignableFrom(clazz)) {
entity = new EntityItem(world, x, y, z, new net.minecraft.server.ItemStack(net.minecraft.server.Item.getItemOf(net.minecraft.server.Blocks.DIRT)));
// Paper end
} else if (FallingBlock.class.isAssignableFrom(clazz)) {
x = location.getBlockX();
y = location.getBlockY();

View File

@ -1,9 +1,6 @@
package org.bukkit.craftbukkit.chunkio;
import net.minecraft.server.Chunk;
import net.minecraft.server.ChunkProviderServer;
import net.minecraft.server.ChunkRegionLoader;
import net.minecraft.server.World;
import net.minecraft.server.*;
import org.bukkit.craftbukkit.util.AsynchronousExecutor;
public class ChunkIOExecutor {
@ -13,7 +10,7 @@ public class ChunkIOExecutor {
private static final AsynchronousExecutor<QueuedChunk, Chunk, Runnable, RuntimeException> instance = new AsynchronousExecutor<>(new ChunkIOProvider(), BASE_THREADS);
public static Chunk syncChunkLoad(World world, ChunkRegionLoader loader, ChunkProviderServer provider, int x, int z) {
return instance.getSkipQueue(new QueuedChunk(x, z, loader, world, provider));
return MCUtil.ensureMain("Async Chunk Load", () -> instance.getSkipQueue(new QueuedChunk(x, z, loader, world, provider))); // Paper
}
public static void queueChunkLoad(World world, ChunkRegionLoader loader, ChunkProviderServer provider, int x, int z, Runnable runnable) {

View File

@ -2,6 +2,7 @@ package org.bukkit.craftbukkit.chunkio;
import java.io.IOException;
import net.minecraft.server.Chunk;
import net.minecraft.server.ChunkCoordIntPair;
import net.minecraft.server.ChunkRegionLoader;
import net.minecraft.server.NBTTagCompound;
@ -33,8 +34,8 @@ class ChunkIOProvider implements AsynchronousExecutor.CallBackProvider<QueuedChu
// sync stuff
public void callStage2(QueuedChunk queuedChunk, Chunk chunk) throws RuntimeException {
if (chunk == null) {
// If the chunk loading failed just do it synchronously (may generate)
if (chunk == null || queuedChunk.provider.chunks.containsKey(ChunkCoordIntPair.a(queuedChunk.x, queuedChunk.z))) { // Paper - also call original if it was already loaded
// If the chunk loading failed (or was already loaded for some reason) just do it synchronously (may generate)
queuedChunk.provider.originalGetChunkAt(queuedChunk.x, queuedChunk.z);
return;
}

View File

@ -23,7 +23,7 @@ public class ConsoleCommandCompleter implements Completer {
return server.getCommandMap().tabComplete(server.getConsoleSender(), buffer);
}
};
this.server.getServer().processQueue.add(waitable);
this.server.getServer().addMainThreadTask(waitable);
try {
List<String> offers = waitable.get();
if (offers == null) {

View File

@ -64,6 +64,16 @@ public class CraftFish extends AbstractProjectile implements Fish {
this.biteChance = chance;
}
// Paper start
@Override
public void remove() {
super.remove();
if (getHandle().owner != null) {
getHandle().owner.hookedFish = null;
}
}
// Paper end
@Deprecated
public LivingEntity _INVALID_getShooter() {
return (LivingEntity) getShooter();

View File

@ -1,10 +1,7 @@
package org.bukkit.craftbukkit.event;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import com.google.common.base.Function;
import com.google.common.base.Functions;
@ -484,6 +481,10 @@ public class CraftEventFactory {
blockDamage = null;
if (source == DamageSource.CACTUS) {
cause = DamageCause.CONTACT;
} else if (source == DamageSource.FIRE) {
cause = DamageCause.FIRE;
} else if (source == DamageSource.LAVA) {
cause = DamageCause.LAVA;
} else {
throw new RuntimeException(String.format("Unhandled damage of %s by %s from %s", entity, damager, source.translationIndex)); // Spigot
}
@ -515,6 +516,8 @@ public class CraftEventFactory {
DamageCause cause = null;
if (source == DamageSource.FIRE) {
cause = DamageCause.FIRE;
} else if (source == DamageSource.LAVA) {
cause = DamageCause.LAVA;
} else if (source == DamageSource.STARVE) {
cause = DamageCause.STARVATION;
} else if (source == DamageSource.WITHER) {
@ -982,4 +985,59 @@ public class CraftEventFactory {
return event;
}
// PaperSpigot end
public static BlockPistonEvent callBlockPistonEvent(final World world, BlockPosition pos, EnumDirection facing, boolean extending) {
// When retracting, PistonExtendsChecker expects the piston arm to already be
// removed, so we have to temporarily remove it or it will get in the way.
BlockPosition extensionPos = pos.shift(facing);
IBlockData extensionData = null;
if(!extending) {
extensionData = world.getType(extensionPos);
world.setTypeAndData(extensionPos, Blocks.AIR.getBlockData(), 0);
}
// This thing searches for all the blocks that will be moved. We have to use it twice,
// because it's too late to cancel by the time NMS calls it. A potential optimization
// would be to save this result and reuse it in the NMS code, but that's probably not
// worth the messy diff.
PistonExtendsChecker checker = new PistonExtendsChecker(world, pos, facing, extending);
boolean moved = checker.a();
if(extensionData != null) {
world.setTypeAndData(extensionPos, extensionData, 0);
}
// This means vanilla mechanics will obstruct the piston, so no need for any event.
if(!moved) return null;
// A sort of lazy list that avoids wrapping blocks until needed
final List movedBlocks = checker.getMovedBlocks();
final List brokenBlocks = checker.getBrokenBlocks();
List<org.bukkit.block.Block> blocks = new AbstractList<Block>() {
@Override
public int size() {
return movedBlocks.size() + brokenBlocks.size();
}
@Override
public org.bukkit.block.Block get(int index) {
if (index >= size() || index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
BlockPosition pos = (BlockPosition) (index < movedBlocks.size() ? movedBlocks.get(index) : brokenBlocks.get(index - movedBlocks.size()));
return world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
}
};
org.bukkit.block.Block block = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
BlockPistonEvent event;
if(extending) {
event = new BlockPistonExtendEvent(block, blocks, CraftBlock.notchToBlockFace(facing));
} else {
event = new BlockPistonRetractEvent(block, blocks, CraftBlock.notchToBlockFace(facing.opposite()));
}
world.getServer().getPluginManager().callEvent(event);
return event;
}
}

View File

@ -196,6 +196,13 @@ public class CraftMetaBanner extends CraftMetaItem implements BannerMeta {
return super.isEmpty() && patterns.isEmpty() && base == null;
}
@Override
public CraftMetaBanner clone() {
CraftMetaBanner meta = (CraftMetaBanner) super.clone();
meta.base = this.base;
meta.patterns = this.patterns;
return meta;
}
@Override
boolean applicableTo(Material type) {

View File

@ -47,7 +47,7 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta
@ItemMetaKey.Specific(ItemMetaKey.Specific.To.NBT)
static final ItemMetaKey BLOCK_ENTITY_TAG = new ItemMetaKey("BlockEntityTag");
final Material material;
Material material;
NBTTagCompound blockEntityTag;
CraftMetaBlockState(CraftMetaItem meta, Material material) {
@ -335,4 +335,12 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta
blockEntityTag = new NBTTagCompound();
te.b(blockEntityTag);
}
@Override
public CraftMetaBlockState clone() {
CraftMetaBlockState meta = (CraftMetaBlockState) super.clone();
meta.material = this.material;
meta.blockEntityTag = this.blockEntityTag;
return meta;
}
}

View File

@ -75,7 +75,7 @@ import net.minecraft.server.IAttribute;
* <li> SerializableMeta.Deserializers deserializer()
*/
@DelegateDeserialization(CraftMetaItem.SerializableMeta.class)
class CraftMetaItem implements ItemMeta, Repairable {
public class CraftMetaItem implements ItemMeta, Repairable {
static class ItemMetaKey {

View File

@ -0,0 +1,127 @@
/*
* Copyright (c) 2018 Daniel Ennis (Aikar) MIT License
*
* 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 org.bukkit.craftbukkit.scheduler;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.bukkit.plugin.Plugin;
import org.github.paperspigot.ServerSchedulerReportingWrapper;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CraftAsyncScheduler extends CraftScheduler {
private final ThreadPoolExecutor executor = new ThreadPoolExecutor(
4, Integer.MAX_VALUE,30L, TimeUnit.SECONDS, new SynchronousQueue<>(),
new ThreadFactoryBuilder().setNameFormat("Craft Scheduler Thread - %1$d").build());
private final Executor management = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder()
.setNameFormat("Craft Async Scheduler Management Thread").build());
private final List<CraftTask> temp = new ArrayList<>();
CraftAsyncScheduler() {
super(true);
executor.allowCoreThreadTimeOut(true);
executor.prestartAllCoreThreads();
}
@Override
public void cancelTask(int taskId) {
this.management.execute(() -> this.removeTask(taskId));
}
private synchronized void removeTask(int taskId) {
parsePending();
this.pending.removeIf((task) -> {
if (task.getTaskId() == taskId) {
task.cancel0();
return true;
}
return false;
});
}
@Override
public void mainThreadHeartbeat(int currentTick) {
this.currentTick = currentTick;
this.management.execute(() -> this.runTasks(currentTick));
}
private synchronized void runTasks(int currentTick) {
parsePending();
while (!this.pending.isEmpty() && this.pending.peek().getNextRun() <= currentTick) {
CraftTask task = this.pending.remove();
if (executeTask(task)) {
final long period = task.getPeriod();
if (period > 0) {
task.setNextRun(currentTick + period);
temp.add(task);
}
}
parsePending();
}
this.pending.addAll(temp);
temp.clear();
}
private boolean executeTask(CraftTask task) {
if (isValid(task)) {
this.runners.put(task.getTaskId(), task);
this.executor.execute(new ServerSchedulerReportingWrapper(task));
return true;
}
return false;
}
@Override
public synchronized void cancelTasks(Plugin plugin) {
parsePending();
for (Iterator<CraftTask> iterator = this.pending.iterator(); iterator.hasNext(); ) {
CraftTask task = iterator.next();
if (task.getTaskId() != -1 && (plugin == null || task.getOwner().equals(plugin))) {
task.cancel0();
iterator.remove();
}
}
}
@Override
public synchronized void cancelAllTasks() {
cancelTasks(null);
}
/**
* Task is not cancelled
* @param runningTask
* @return
*/
static boolean isValid(CraftTask runningTask) {
return runningTask.getPeriod() >= CraftTask.NO_REPEATING;
}
}

View File

@ -21,6 +21,9 @@ import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
import org.bukkit.scheduler.BukkitWorker;
import org.github.paperspigot.ServerSchedulerReportingWrapper;
import org.github.paperspigot.event.ServerExceptionEvent;
import org.github.paperspigot.exception.ServerSchedulerException;
/**
* The fundamental concepts for this implementation:
@ -32,7 +35,7 @@ import org.bukkit.scheduler.BukkitWorker;
* <li>Changing the period on a task is delicate.
* Any future task needs to notify waiting threads.
* Async tasks must be synchronized to make sure that any thread that's finishing will remove itself from {@link #runners}.
* Another utility method is provided for this, {@link #cancelTask(int)}</li>
* Another utility method is provided for this, {@link #cancelTask(CraftTask)}</li>
* <li>{@link #runners} provides a moderately up-to-date view of active tasks.
* If the linked head to tail set is read, all remaining tasks that were active at the time execution started will be located in runners.</li>
* <li>Async tasks are responsible for removing themselves from runners</li>
@ -47,28 +50,32 @@ public class CraftScheduler implements BukkitScheduler {
*/
private final AtomicInteger ids = new AtomicInteger(1);
/**
* Current head of linked-list. This reference is always stale, {@code CraftTask#next} is the live reference.
* Current head of linked-list. This reference is always stale, {@link CraftTask#next} is the live reference.
*/
private volatile CraftTask head = new CraftTask();
/**
* Tail of a linked-list. AtomicReference only matters when adding to queue
*/
private final AtomicReference<CraftTask> tail = new AtomicReference<>(head);
private final AtomicReference<CraftTask> tail = new AtomicReference<CraftTask>(head);
/**
* Main thread logic only
*/
private final PriorityQueue<CraftTask> pending = new PriorityQueue<>(10,
(o1, o2) -> (int) (o1.getNextRun() - o2.getNextRun()));
final PriorityQueue<CraftTask> pending = new PriorityQueue<CraftTask>(10, // Paper
new Comparator<CraftTask>() {
public int compare(final CraftTask o1, final CraftTask o2) {
return (int) (o1.getNextRun() - o2.getNextRun());
}
});
/**
* Main thread logic only
*/
private final List<CraftTask> temp = new ArrayList<>();
private final List<CraftTask> temp = new ArrayList<CraftTask>();
/**
* These are tasks that are currently active. It's provided for 'viewing' the current state.
*/
private final ConcurrentHashMap<Integer, CraftTask> runners = new ConcurrentHashMap<>();
private volatile int currentTick = -1;
private final Executor executor = Executors.newCachedThreadPool(new com.google.common.util.concurrent.ThreadFactoryBuilder().setNameFormat("Craft Scheduler Thread - %1$d").build()); // Spigot
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
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;
@ -77,6 +84,23 @@ public class CraftScheduler implements BukkitScheduler {
RECENT_TICKS = 30;
}
// Paper start
private final CraftScheduler asyncScheduler;
private final boolean isAsyncScheduler;
public CraftScheduler() {
this(false);
}
public CraftScheduler(boolean isAsync) {
this.isAsyncScheduler = isAsync;
if (isAsync) {
this.asyncScheduler = this;
} else {
this.asyncScheduler = new CraftAsyncScheduler();
}
}
// Paper end
public int scheduleSyncDelayedTask(final Plugin plugin, final Runnable task) {
return this.scheduleSyncDelayedTask(plugin, task, 0l);
}
@ -143,12 +167,12 @@ public class CraftScheduler implements BukkitScheduler {
} else if (period < -1l) {
period = -1l;
}
return handle(new CraftAsyncTask(runners, plugin, runnable, nextId(), period), delay);
return handle(new CraftAsyncTask(this.asyncScheduler.runners, plugin, runnable, nextId(), period), delay); // Paper
}
public <T> Future<T> callSyncMethod(final Plugin plugin, final Callable<T> task) {
validate(plugin, task);
final CraftFuture<T> future = new CraftFuture<>(task, plugin, nextId());
final CraftFuture<T> future = new CraftFuture<T>(task, plugin, nextId());
handle(future, 0l);
return future;
}
@ -157,6 +181,11 @@ public class CraftScheduler implements BukkitScheduler {
if (taskId <= 0) {
return;
}
// Paper start
if (!this.isAsyncScheduler) {
this.asyncScheduler.cancelTask(taskId);
}
// Paper end
CraftTask task = runners.get(taskId);
if (task != null) {
task.cancel0();
@ -196,6 +225,11 @@ public class CraftScheduler implements BukkitScheduler {
public void cancelTasks(final Plugin plugin) {
Validate.notNull(plugin, "Cannot cancel tasks of null plugin");
// Paper start
if (!this.isAsyncScheduler) {
this.asyncScheduler.cancelTasks(plugin);
}
// Paper end
final CraftTask task = new CraftTask(
new Runnable() {
public void run() {
@ -233,18 +267,25 @@ public class CraftScheduler implements BukkitScheduler {
}
public void cancelAllTasks() {
// Paper start
if (!this.isAsyncScheduler) {
this.asyncScheduler.cancelAllTasks();
}
// Paper end
final CraftTask task = new CraftTask(
() -> {
new Runnable() {
public void run() {
Iterator<CraftTask> it = CraftScheduler.this.runners.values().iterator();
while (it.hasNext()) {
CraftTask task1 = it.next();
task1.cancel0();
if (task1.isSync()) {
CraftTask task = it.next();
task.cancel0();
if (task.isSync()) {
it.remove();
}
}
CraftScheduler.this.pending.clear();
CraftScheduler.this.temp.clear();
}
}){{this.timings=co.aikar.timings.SpigotTimings.getCancelTasksTimer();}}; // Spigot
handle(task, 0l);
for (CraftTask taskPending = head.getNext(); taskPending != null; taskPending = taskPending.getNext()) {
@ -259,6 +300,13 @@ public class CraftScheduler implements BukkitScheduler {
}
public boolean isCurrentlyRunning(final int taskId) {
// Paper start
if (!isAsyncScheduler) {
if (this.asyncScheduler.isCurrentlyRunning(taskId)) {
return true;
}
}
// Paper end
final CraftTask task = runners.get(taskId);
if (task == null || task.isSync()) {
return false;
@ -273,6 +321,11 @@ public class CraftScheduler implements BukkitScheduler {
if (taskId <= 0) {
return false;
}
// Paper start
if (!this.isAsyncScheduler && this.asyncScheduler.isQueued(taskId)) {
return true;
}
// Paper end
for (CraftTask task = head.getNext(); task != null; task = task.getNext()) {
if (task.getTaskId() == taskId) {
return task.getPeriod() >= -1l; // The task will run
@ -283,7 +336,13 @@ public class CraftScheduler implements BukkitScheduler {
}
public List<BukkitWorker> getActiveWorkers() {
final ArrayList<BukkitWorker> workers = new ArrayList<>();
// Paper start
if (!isAsyncScheduler) {
//noinspection TailRecursion
return this.asyncScheduler.getActiveWorkers();
}
// Paper end
final ArrayList<BukkitWorker> workers = new ArrayList<BukkitWorker>();
for (final CraftTask taskObj : runners.values()) {
// Iterator will be a best-effort (may fail to grab very new values) if called from an async thread
if (taskObj.isSync()) {
@ -299,7 +358,7 @@ public class CraftScheduler implements BukkitScheduler {
}
public List<BukkitTask> getPendingTasks() {
final ArrayList<CraftTask> truePending = new ArrayList<>();
final ArrayList<CraftTask> truePending = new ArrayList<CraftTask>();
for (CraftTask task = head.getNext(); task != null; task = task.getNext()) {
if (task.getTaskId() != -1) {
// -1 is special code
@ -307,7 +366,7 @@ public class CraftScheduler implements BukkitScheduler {
}
}
final ArrayList<BukkitTask> pending = new ArrayList<>();
final ArrayList<BukkitTask> pending = new ArrayList<BukkitTask>();
for (CraftTask task : runners.values()) {
if (task.getPeriod() >= -1l) {
pending.add(task);
@ -319,6 +378,11 @@ public class CraftScheduler implements BukkitScheduler {
pending.add(task);
}
}
// Paper start
if (!this.isAsyncScheduler) {
pending.addAll(this.asyncScheduler.getPendingTasks());
}
// Paper end
return pending;
}
@ -326,6 +390,11 @@ public class CraftScheduler implements BukkitScheduler {
* This method is designed to never block or wait for locks; an immediate execution of all current tasks.
*/
public void mainThreadHeartbeat(final int currentTick) {
// Paper start
if (!this.isAsyncScheduler) {
this.asyncScheduler.mainThreadHeartbeat(currentTick);
}
// Paper end
this.currentTick = currentTick;
final List<CraftTask> temp = this.temp;
parsePending();
@ -342,18 +411,24 @@ public class CraftScheduler implements BukkitScheduler {
try {
task.run();
} catch (final Throwable throwable) {
task.getOwner().getLogger().log(
Level.WARNING,
String.format(
// Paper start
String msg = String.format(
"Task #%s for %s generated an exception",
task.getTaskId(),
task.getOwner().getDescription().getFullName()),
task.getOwner().getDescription().getFullName());
task.getOwner().getLogger().log(
Level.WARNING,
msg,
throwable);
task.getOwner().getServer().getPluginManager().callEvent(
new ServerExceptionEvent(new ServerSchedulerException(msg, throwable, task))
);
// Paper end
}
parsePending();
} else {
debugTail = debugTail.setNext(new CraftAsyncDebugger(currentTick + RECENT_TICKS, task.getOwner(), task.getTaskClass()));
executor.execute(task);
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)
}
@ -370,7 +445,7 @@ public class CraftScheduler implements BukkitScheduler {
debugHead = debugHead.getNextHead(currentTick);
}
private void addTask(final CraftTask task) {
protected void addTask(final CraftTask task) {
final AtomicReference<CraftTask> tail = this.tail;
CraftTask tailTask = tail.get();
while (!tail.compareAndSet(tailTask, task)) {
@ -379,7 +454,13 @@ public class CraftScheduler implements BukkitScheduler {
tailTask.setNext(task);
}
private CraftTask handle(final CraftTask task, final long delay) {
protected CraftTask handle(final CraftTask task, final long delay) { // Paper
// Paper start
if (!this.isAsyncScheduler && !task.isSync()) {
this.asyncScheduler.handle(task, delay);
return task;
}
// Paper end
task.setNextRun(currentTick + delay);
addTask(task);
return task;
@ -397,7 +478,7 @@ public class CraftScheduler implements BukkitScheduler {
return ids.incrementAndGet();
}
private void parsePending() {
void parsePending() { // Paper
CraftTask head = this.head;
CraftTask task = head.getNext();
CraftTask lastTask = head;

View File

@ -10,6 +10,11 @@ import org.bukkit.scheduler.BukkitTask;
public class CraftTask implements BukkitTask, Runnable { // Spigot
private volatile CraftTask next = null;
public static final int ERROR = 0;
public static final int NO_REPEATING = -1;
public static final int CANCEL = -2;
public static final int PROCESS_FOR_FUTURE = -3;
public static final int DONE_FOR_FUTURE = -4;
/**
* -1 means no repeating <br>
* -2 means cancel <br>

View File

@ -144,8 +144,14 @@ public final class CraftChatMessage {
public static String fromComponent(IChatBaseComponent component, EnumChatFormat defaultColor) {
if (component == null) return "";
StringBuilder out = new StringBuilder();
// SportPaper - limit iterations to 2
byte iterations = 0;
for (IChatBaseComponent c : (Iterable<IChatBaseComponent>) component) {
if (++iterations > 2) {
break;
}
for (IChatBaseComponent c : component) {
ChatModifier modi = c.getChatModifier();
out.append(modi.getColor() == null ? defaultColor : modi.getColor());
if (modi.isBold()) {

View File

@ -8,7 +8,7 @@ import java.util.TreeSet;
public class HashTreeSet<V> implements Set<V> {
private HashSet<V> hash = new HashSet<>();
private Set<V> hash = new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<V>(); //Paper - Replace java.util.HashSet with ObjectOpenHashSet
private TreeSet<V> tree = new TreeSet<>();
public HashTreeSet() {

View File

@ -146,13 +146,13 @@ public class PaperSpigotConfig
public static double babyZombieMovementSpeed;
private static void babyZombieMovementSpeed()
{
babyZombieMovementSpeed = getDouble( "settings.baby-zombie-movement-speed", 0.5D ); // Player moves at 0.1F, for reference
babyZombieMovementSpeed = getDouble( "baby-zombie-movement-speed", 0.5D ); // Player moves at 0.1F, for reference
}
public static boolean interactLimitEnabled;
private static void interactLimitEnabled()
{
interactLimitEnabled = getBoolean( "settings.limit-player-interactions", true );
interactLimitEnabled = getBoolean( "limit-player-interactions", true );
if ( !interactLimitEnabled )
{
Bukkit.getLogger().log( Level.INFO, "Disabling player interaction limiter, your server may be more vulnerable to malicious users" );
@ -223,12 +223,38 @@ public class PaperSpigotConfig
warnForExcessiveVelocity = getBoolean("warnWhenSettingExcessiveVelocity", true);
}
public static int regionFileCacheSize = 256;
private static void regionFileCacheSize() {
regionFileCacheSize = getInt("region-file-cache-size", 256);
}
public static boolean saveEmptyScoreboardTeams;
private static void saveEmptyScoreboardTeams() {
saveEmptyScoreboardTeams = getBoolean("save-empty-scoreboard-teams", false);
}
// KigPaper start
public static int tabSpamIncrement = 10;
public static int tabSpamLimit = 500;
private static void tabSpamLimiters() {
tabSpamIncrement = getInt("spam-limiter.tab-spam-increment", tabSpamIncrement);
tabSpamLimit = getInt("spam-limiter.tab-spam-limit", tabSpamLimit);
}
public static boolean includeRandomnessInArrowTrajectory = false;
private static void includeRandomnessInArrowTrajectory() {
includeRandomnessInArrowTrajectory = getBoolean("include-randomness-in-arrow-trajectory", includeRandomnessInArrowTrajectory);
}
public static boolean includeRandomnessInArrowDamage = true;
private static void includeRandomnessInArrowDamage() {
includeRandomnessInArrowDamage = getBoolean("include-randomness-in-arrow-damage", includeRandomnessInArrowDamage);
}
public static boolean tickEmptyWorlds = true;
private static void tickEmptyWorlds() {
tickEmptyWorlds = getBoolean("tick-empty-worlds", tickEmptyWorlds);
}
public static boolean betterVehicleHitboxes;
private static void betterVehicleHitboxes() {
betterVehicleHitboxes = getBoolean("better-vehicle-hitboxes", true);
@ -263,5 +289,4 @@ public class PaperSpigotConfig
private static void kickChatMessageLength() {
kickChatMessageLength = getBoolean("kick-chat-message-length", false);
}
// KigPaper end
}

View File

@ -402,4 +402,14 @@ public class PaperSpigotWorldConfig
{
portalSearchRadius = getInt("portal-search-radius", 128);
}
public boolean removeCorruptTEs;
private void removeCorruptTEs() {
removeCorruptTEs = getBoolean("remove-corrupt-tile-entities", false);
}
public boolean armorStandEntityLookups;
private void armorStandEntityLookups() {
armorStandEntityLookups = getBoolean("armor-stands-do-collision-entity-lookups", true);
}
}

View File

@ -0,0 +1,38 @@
package org.github.paperspigot;
import com.google.common.base.Preconditions;
import org.bukkit.craftbukkit.scheduler.CraftTask;
import org.github.paperspigot.event.ServerExceptionEvent;
import org.github.paperspigot.exception.ServerSchedulerException;
/**
* Reporting wrapper to catch exceptions not natively
*/
public class ServerSchedulerReportingWrapper implements Runnable {
private final CraftTask internalTask;
public ServerSchedulerReportingWrapper(CraftTask internalTask) {
this.internalTask = Preconditions.checkNotNull(internalTask, "internalTask");
}
@Override
public void run() {
try {
internalTask.run();
} catch (RuntimeException e) {
internalTask.getOwner().getServer().getPluginManager().callEvent(
new ServerExceptionEvent(new ServerSchedulerException(e, internalTask))
);
throw e;
} catch (Throwable t) {
internalTask.getOwner().getServer().getPluginManager().callEvent(
new ServerExceptionEvent(new ServerSchedulerException(t, internalTask))
); //Do not rethrow, since it is not permitted with Runnable#run
}
}
public CraftTask getInternalTask() {
return internalTask;
}
}

View File

@ -126,9 +126,11 @@ public class ActivationRange
{
for ( int j1 = k; j1 <= l; ++j1 )
{
if ( world.getWorld().isChunkLoaded( i1, j1 ) )
Chunk chunk = world.getChunkIfLoaded( i1, j1 ); // SportPaper - remove double chunk lookup
if ( chunk != null )
{
activateChunkEntities( world.getChunkAt( i1, j1 ) );
activateChunkEntities( chunk );
}
}
}

View File

@ -23,7 +23,7 @@ public class RestartCommand extends Command
{
if ( testPermission( sender ) )
{
MinecraftServer.getServer().processQueue.add(RestartCommand::restart);
MinecraftServer.getServer().addMainThreadTask(RestartCommand::restart);
}
return true;
}

View File

@ -52,7 +52,7 @@ public class WatchdogThread extends Thread
while ( !stopping )
{
//
if ( lastTick != 0 && System.currentTimeMillis() > lastTick + timeoutTime )
if ( lastTick != 0 && System.currentTimeMillis() > lastTick + timeoutTime && !Boolean.getBoolean("disable.watchdog")) // Paper - Add property to disable
{
Logger log = Bukkit.getServer().getLogger();
log.log( Level.SEVERE, "The server has stopped responding!" );

View File

@ -3,10 +3,10 @@
<Appenders>
<Console name="WINDOWS_COMPAT" target="SYSTEM_OUT"></Console>
<Queue name="TerminalConsole">
<PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n" />
<PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg{nolookups}%n" />
</Queue>
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg%n" />
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg{nolookups}%n" />
<Policies>
<TimeBasedTriggeringPolicy />
<OnStartupTriggeringPolicy />