So many more updates

This commit is contained in:
beaness 2022-06-25 22:03:19 +02:00
parent be7deb2637
commit 506bf650d5
58 changed files with 2259 additions and 1358 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -106,4 +106,50 @@ public class eSpigotConfig
{ {
obfuscatePlayerHealth = getBoolean( "settings.obfuscate-player-health", true ); obfuscatePlayerHealth = getBoolean( "settings.obfuscate-player-health", true );
} }
public static boolean fireEntityExplodeEvent;
private static void fireEntityExplodeEvent()
{
fireEntityExplodeEvent = getBoolean( "settings.fire-entity-explode-event", true );
}
public static boolean fastMath;
public static boolean fastMathCosSin;
private static void fastMath()
{
fastMath = getBoolean( "settings.fast-math", false );
fastMathCosSin = getBoolean( "settings.fast-math-cos-sin", false );
}
public static boolean pandaWire;
private static void pandaWire()
{
pandaWire = getBoolean( "settings.panda-wire", true );
}
public static boolean lagCompensatedPotions;
public static boolean lagCompensatedPearls;
private static void lagCompensate() {
lagCompensatedPotions = getBoolean("settings.lag-compensated.potions", true);
lagCompensatedPearls = getBoolean("settings.lag-compensated.pearls", true);
}
public static boolean cannonTracker;
private static void cannonTracker()
{
cannonTracker = getBoolean( "settings.cannon-tracker", false );
}
public static boolean fixedPoolForTNT;
private static void fixedPoolForTNT()
{
fixedPoolForTNT = getBoolean( "tnt.fixed-pool", false );
}
public static int fixedPoolSizeForTNT;
private static void fixedPoolSizeForTNT()
{
fixedPoolSizeForTNT = getInt( "tnt.fixed-pool-size", 500 );
}
} }

View File

@ -2,6 +2,7 @@ package com.elevatemc.spigot;
import com.elevatemc.spigot.command.KnockbackCommand; import com.elevatemc.spigot.command.KnockbackCommand;
import com.elevatemc.spigot.command.TicksPerSecondCommand; import com.elevatemc.spigot.command.TicksPerSecondCommand;
import com.elevatemc.spigot.config.eSpigotConfig;
import com.elevatemc.spigot.handler.MovementHandler; import com.elevatemc.spigot.handler.MovementHandler;
import com.elevatemc.spigot.handler.PacketHandler; import com.elevatemc.spigot.handler.PacketHandler;
import com.elevatemc.spigot.util.YamlConfig; import com.elevatemc.spigot.util.YamlConfig;
@ -34,6 +35,7 @@ public class eSpigot {
public eSpigot(CraftServer server) { public eSpigot(CraftServer server) {
// Set instance of the server // Set instance of the server
instance = this; instance = this;
knockbackConfig = new YamlConfig("knockback.yml"); knockbackConfig = new YamlConfig("knockback.yml");
knockbackHandler = new KnockbackHandler(); knockbackHandler = new KnockbackHandler();

View File

@ -1,5 +1,5 @@
// Taken from https://github.com/Elierrr/NachoSpigot/blob/b52603b25ff32b54c0b8df769e2271d187f92dc8/NachoSpigot-Server/src/main/java/me/elier/minecraft/util/CryptException.java // Taken from https://github.com/Elierrr/NachoSpigot/blob/b52603b25ff32b54c0b8df769e2271d187f92dc8/NachoSpigot-Server/src/main/java/me/elier/minecraft/util/CryptException.java
package com.elevatemc.spigot.util; package com.elevatemc.spigot.exception;
public class CryptException extends Exception { public class CryptException extends Exception {
public CryptException(Throwable throwable) { public CryptException(Throwable throwable) {

View File

@ -0,0 +1,7 @@
package com.elevatemc.spigot.exception;
public class ExploitException extends RuntimeException {
public ExploitException(String message) {
super(message);
}
}

View File

@ -0,0 +1,53 @@
package com.elevatemc.spigot.network;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.timeout.ReadTimeoutHandler;
import net.minecraft.server.*;
import org.github.paperspigot.PaperSpigotConfig;
public class MinecraftPipeline extends ChannelInitializer<SocketChannel>
{
private final ServerConnection serverConnection;
public MinecraftPipeline(ServerConnection serverConnection) {
this.serverConnection = serverConnection;
}
protected void initChannel(SocketChannel channel) {
ChannelConfig config = channel.config();
try {
config.setOption(ChannelOption.SO_KEEPALIVE, true);
} catch (Exception ignored) {}
try {
config.setOption(ChannelOption.TCP_NODELAY, true);
} catch (Exception ignored) {}
try {
config.setOption(ChannelOption.TCP_FASTOPEN, 1);
} catch (Exception ignored) {}
try {
config.setOption(ChannelOption.TCP_FASTOPEN_CONNECT, true);
} catch (Exception ignored) {}
try {
config.setOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
} catch (Exception ignored) {}
try {
config.setOption(ChannelOption.IP_TOS, 0x18);
} catch (Exception ignored) {}
if(PaperSpigotConfig.nettyReadTimeout) channel.pipeline().addLast("timeout", new ReadTimeoutHandler(30));
channel.pipeline()
.addLast("legacy_query", new LegacyPingHandler(serverConnection))
.addLast("splitter", new PacketSplitter())
.addLast("decoder", new PacketDecoder(EnumProtocolDirection.SERVERBOUND))
.addLast("prepender", PacketPrepender.INSTANCE) // PandaSpigot - Share PacketPrepender instance
.addLast("encoder", new PacketEncoder(EnumProtocolDirection.CLIENTBOUND));
NetworkManager networkmanager = new NetworkManager(EnumProtocolDirection.SERVERBOUND);
this.serverConnection.pending.add(networkmanager); // Paper
channel.pipeline().addLast("packet_handler", networkmanager);
networkmanager.a(new HandshakeListener(this.serverConnection.server, networkmanager));
}
}

View File

@ -0,0 +1,484 @@
package com.elevatemc.spigot.redstone;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import net.minecraft.server.*;
import org.apache.commons.lang3.ArrayUtils;
import org.bukkit.event.block.BlockRedstoneEvent;
import java.lang.reflect.Field;
import java.util.*;
/*
* Based on https://gist.github.com/Panda4994/70ed6d39c89396570e062e4404a8d518
* https://www.spigotmc.org/resources/pandawire-1-8-8-1-15-2.41991/
*
* Mainly based on a decompiled version of PandaWire, with comments and structure
* from the original copied over. This get rid of any errors the decompiler made.
*
* On top of that theres additional optimisations in places.
*/
public class PandaRedstoneWire extends BlockRedstoneWire {
/*
* I considered replacing the lists with LinkedHashSets for faster lookup,
* but an artificial test showed barely any difference in performance
*/
/** Positions that need to be turned off **/
private List<BlockPosition> turnOff = Lists.newArrayList();
/** Positions that need to be checked to be turned on **/
private List<BlockPosition> turnOn = Lists.newArrayList();
/** Positions of wire that was updated already (Ordering determines update order and is therefore required!) **/
private final Set<BlockPosition> updatedRedstoneWire = Sets.newLinkedHashSet();
/** Ordered arrays of the facings; Needed for the update order.
* I went with a vertical-first order here, but vertical last would work to.
* However it should be avoided to update the vertical axis between the horizontal ones as this would cause unneeded directional behavior. **/
private static final EnumDirection[] facingsHorizontal = {EnumDirection.WEST, EnumDirection.EAST, EnumDirection.NORTH, EnumDirection.SOUTH};
private static final EnumDirection[] facingsVertical = {EnumDirection.DOWN, EnumDirection.UP};
private static final EnumDirection[] facings = ArrayUtils.addAll(facingsVertical, facingsHorizontal);
/** Offsets for all surrounding blocks that need to receive updates **/
private static final BaseBlockPosition[] surroundingBlocksOffset;
static {
Set<BaseBlockPosition> set = Sets.newLinkedHashSet();
for (EnumDirection facing : facings) {
set.add(ReflectUtil.getOfT(facing, BaseBlockPosition.class));
}
for (EnumDirection facing1 : facings) {
BaseBlockPosition v1 = ReflectUtil.getOfT(facing1, BaseBlockPosition.class);
for (EnumDirection facing2 : facings) {
BaseBlockPosition v2 = ReflectUtil.getOfT(facing2, BaseBlockPosition.class);
set.add(new BlockPosition(v1.getX() + v2.getX(), v1.getY() + v2.getY(), v1.getZ() + v2.getZ()));
}
}
set.remove(BlockPosition.ZERO);
surroundingBlocksOffset = set.toArray(new BaseBlockPosition[0]);
}
private boolean canProvidePower = true;
public PandaRedstoneWire() {
super();
}
private void updateSurroundingRedstone(World worldIn, BlockPosition pos, IBlockData iblockdata) {
// Recalculate the connected wires
calculateCurrentChanges(worldIn, pos, iblockdata);
// Set to collect all the updates, to only execute them once. Ordering required.
Set<BlockPosition> blocksNeedingUpdate = Sets.newLinkedHashSet();
// Add the needed updates
for (BlockPosition posi : updatedRedstoneWire) {
addBlocksNeedingUpdate(worldIn, posi, blocksNeedingUpdate);
}
// Add all other updates to keep known behaviors
// They are added in a backwards order because it preserves a commonly used behavior with the update order
Iterator<BlockPosition> it = Lists.newLinkedList(updatedRedstoneWire).descendingIterator();
while (it.hasNext()) {
addAllSurroundingBlocks(it.next(), blocksNeedingUpdate);
}
// Remove updates on the wires as they just were updated
blocksNeedingUpdate.removeAll(updatedRedstoneWire);
/* Avoid unnecessary updates on the just updated wires
* A huge scale test showed about 40% more ticks per second
* It's probably less in normal usage but likely still worth it
*/
updatedRedstoneWire.clear();
// Execute updates
for (BlockPosition posi : blocksNeedingUpdate) {
worldIn.d(posi, this);
}
}
/**
* Turns on or off all connected wires
*
* @param worldIn World
* @param position Position of the wire that received the update
* @param state Current state of this block
*/
protected void calculateCurrentChanges(World worldIn, BlockPosition position, IBlockData state) {
// Turn off all connected wires first if needed
if (state.getBlock() == this) {
this.turnOff.add(position);
} else {
// In case this wire was removed, check the surrounding wires
checkSurroundingWires(worldIn, position);
}
while (!turnOff.isEmpty()) {
BlockPosition pos = turnOff.remove(0);
state = worldIn.getType(pos);
int oldPower = state.get(POWER);
this.canProvidePower = false;
int blockPower = worldIn.A(pos);
this.canProvidePower = true;
int wirePower = getSurroundingWirePower(worldIn, pos);
// Lower the strength as it moved a block
--wirePower;
int newPower = Math.max(blockPower, wirePower);
// Power lowered?
if (newPower < oldPower) {
// If it's still powered by a direct source (but weaker) mark for turn on
if (blockPower > 0 && !turnOn.contains(pos)) {
turnOn.add(pos);
}
// Set all the way to off for now, because wires that were powered by this need to update first
setWireState(worldIn, pos, state, 0);
// Power rose?
} else if (newPower > oldPower) {
// Set new Power
setWireState(worldIn, pos, state, newPower);
}
// Check if surrounding wires need to change based on the current/new state and add them to the lists
checkSurroundingWires(worldIn, pos);
}
while (!turnOn.isEmpty()) {
BlockPosition pos = turnOn.remove(0);
state = worldIn.getType(pos);
int oldPower = state.get(POWER);
this.canProvidePower = false;
int blockPower = worldIn.A(pos);
this.canProvidePower = true;
int wirePower = getSurroundingWirePower(worldIn, pos);
// Lower the strength as it moved a block
wirePower--;
int newPower = Math.max(blockPower, wirePower);
if (oldPower != newPower) {
BlockRedstoneEvent event = new BlockRedstoneEvent(
worldIn.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()),
oldPower,
newPower);
worldIn.getServer().getPluginManager().callEvent(event);
newPower = event.getNewCurrent();
}
if (newPower > oldPower) {
setWireState(worldIn, pos, state, newPower);
}
// Check if surrounding wires need to change based on the current/new state and add them to the lists
checkSurroundingWires(worldIn, pos);
}
turnOff.clear();
}
/**
* Checks if an wire needs to be marked for update depending on the power next to it
*
* @author panda
*
* @param worldIn World
* @param pos Position of the wire that might need to change
* @param otherPower Power of the wire next to it
*/
protected void addWireToList(World worldIn, BlockPosition pos, int otherPower) {
IBlockData state = worldIn.getType(pos);
if (state.getBlock() == this) {
int power = state.get(POWER);
// Could get powered stronger by the neighbor?
if (power < otherPower - 1 && !turnOn.contains(pos)) {
// Mark for turn on check.
turnOn.add(pos);
}
// Should have powered the neighbor? Probably was powered by it and is in turn off phase.
if (power > otherPower && !turnOff.contains(pos)) {
// Mark for turn off check.
turnOff.add(pos);
}
}
}
/**
* Checks if the wires around need to get updated depending on this wires state.
* Checks all wires below before the same layer before on top to keep
* some more rotational symmetry around the y-axis.
*
* @author panda
*
* @param worldIn World
* @param pos Position of the wire
*/
protected void checkSurroundingWires(World worldIn, BlockPosition pos) {
IBlockData state = worldIn.getType(pos);
int ownPower = 0;
if (state.getBlock() == Blocks.REDSTONE_WIRE) {
ownPower = state.get(POWER);
}
// Check wires on the same layer first as they appear closer to the wire
for (EnumDirection facingHorizontal : facingsHorizontal) {
this.addWireToList(worldIn, pos.shift(facingHorizontal), ownPower);
}
for (EnumDirection facingVertical : facingsVertical) {
BlockPosition offsetPos = pos.shift(facingVertical);
Block block = worldIn.getType(offsetPos).getBlock();
boolean solidBlock = block.u();
for (EnumDirection facingHorizontal : facingsHorizontal) {
// wire can travel upwards if the block on top doesn't cut the wire (is non-solid)
// it can travel down if the block below is solid and the block "diagonal" doesn't cut off the wire (is non-solid)
if (facingVertical == EnumDirection.UP && (!solidBlock || /* This can improve glowstone wiring up to 2.5x */ block == Blocks.GLOWSTONE) || facingVertical == EnumDirection.DOWN && solidBlock && !worldIn.getType(offsetPos.shift(facingHorizontal)).getBlock().isOccluding()) {
this.addWireToList(worldIn, offsetPos.shift(facingHorizontal), ownPower);
}
}
}
}
/**
* Gets the maximum power of the surrounding wires
*
* @author panda
*
* @param worldIn World
* @param pos Position of the asking wire
* @return The maximum power of the wires that could power the wire at pos
*/
private int getSurroundingWirePower(World worldIn, BlockPosition pos) {
int wirePower = 0;
for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
BlockPosition offsetPos = pos.shift(enumfacing);
IBlockData iblockdata = worldIn.getType(offsetPos);
boolean occluding = iblockdata.getBlock().isOccluding();
// Wires on the same layer
wirePower = this.getPower(iblockdata, wirePower);
// Block below the wire need to be solid (Upwards diode of slabs/stairs/glowstone) and no block should cut the wire
if (occluding && !worldIn.getType(pos.up()).getBlock().isOccluding()) {
wirePower = this.getPower(worldIn, offsetPos.up(), wirePower);
// Only get from power below if no block is cutting the wire
} else if (!occluding) {
wirePower = this.getPower(worldIn, offsetPos.down(), wirePower);
}
}
return wirePower;
}
/**
* Adds all blocks that need to receive an update from a redstone change in this position.
* This means only blocks that actually could change.
*
* @author panda
*
* @param worldIn World
* @param pos Position of the wire
* @param set Set to add the update positions too
*/
private void addBlocksNeedingUpdate(World worldIn, BlockPosition pos, Set<BlockPosition> set) {
Set<EnumDirection> connectedSides = getSidesToPower(worldIn, pos);
// Add the blocks next to the wire first (closest first order)
for (EnumDirection facing : facings) {
BlockPosition offsetPos = pos.shift(facing);
IBlockData state = worldIn.getType(offsetPos);
// canConnectTo() is not the nicest solution here as it returns true for e.g. the front of a repeater
// canBlockBePowereFromSide catches these cases
boolean flag = connectedSides.contains(facing.opposite()) || facing == EnumDirection.DOWN;
if (flag || (facing.k().c() && a(state, facing))) {
if (canBlockBePoweredFromSide(state, facing, true)) set.add(offsetPos);
}
// Later add blocks around the surrounding blocks that get powered
if (flag && worldIn.getType(offsetPos).getBlock().isOccluding()) {
for (EnumDirection facing1 : facings) {
if (canBlockBePoweredFromSide(worldIn.getType(offsetPos.shift(facing1)), facing1, false)) set.add(offsetPos.shift(facing1));
}
}
}
}
/**
* Checks if a block can get powered from a side.
* This behavior would better be implemented per block type as follows:
* - return false as default. (blocks that are not affected by redstone don't need to be updated, it doesn't really hurt if they are either)
* - return true for all blocks that can get powered from all side and change based on it (doors, fence gates, trap doors, note blocks, lamps, dropper, hopper, TNT, rails, possibly more)
* - implement own logic for pistons, repeaters, comparators and redstone torches
* The current implementation was chosen to keep everything in one class.
*
* Why is this extra check needed?
* 1. It makes sure that many old behaviors still work (QC + Pistons).
* 2. It prevents updates from "jumping".
* Or rather it prevents this wire to update a block that would get powered by the next one of the same line.
* This is to prefer as it makes understanding the update order of the wire really easy. The signal "travels" from the power source.
*
* @author panda
*
* @param state State of the block
* @param side Side from which it gets powered
* @param isWire True if it's powered by a wire directly, False if through a block
* @return True if the block can change based on the power level it gets on the given side, false otherwise
*/
private boolean canBlockBePoweredFromSide(IBlockData state, EnumDirection side, boolean isWire) {
Block block = state.getBlock();
if (block == Blocks.AIR) return false;
if (block instanceof BlockPiston && state.get(BlockPiston.FACING) == side.opposite()) return false;
if (block instanceof BlockDiodeAbstract && state.get(BlockDiodeAbstract.FACING) != side.opposite())
return isWire && block instanceof BlockRedstoneComparator && state.get(BlockRedstoneComparator.FACING).k() != side.k() && side.k().c();
return !(state.getBlock() instanceof BlockRedstoneTorch) || (!isWire && state.get(BlockRedstoneTorch.FACING) == side);
}
/**
* Creates a list of all horizontal sides that can get powered by a wire.
* The list is ordered the same as the facingsHorizontal.
*
* @param worldIn World
* @param pos Position of the wire
* @return List of all facings that can get powered by this wire
*/
private Set<EnumDirection> getSidesToPower(World worldIn, BlockPosition pos) {
Set<EnumDirection> retval = Sets.newHashSet();
for (EnumDirection facing : facingsHorizontal) {
if (this.isPowerSourceAt(worldIn, pos, facing)) {
retval.add(facing);
}
}
if (retval.isEmpty()) return Sets.newHashSet(facingsHorizontal);
boolean northsouth = retval.contains(EnumDirection.NORTH) || retval.contains(EnumDirection.SOUTH);
boolean eastwest = retval.contains(EnumDirection.EAST) || retval.contains(EnumDirection.WEST);
if (northsouth) {
retval.remove(EnumDirection.EAST);
retval.remove(EnumDirection.WEST);
}
if (eastwest) {
retval.remove(EnumDirection.NORTH);
retval.remove(EnumDirection.SOUTH);
}
return retval;
}
private boolean canSidePower(World worldIn, BlockPosition pos, EnumDirection side) {
Set<EnumDirection> retval = Sets.newHashSet();
for (EnumDirection facing : facingsHorizontal) {
if (this.isPowerSourceAt(worldIn, pos, facing)) {
retval.add(facing);
}
}
if (retval.isEmpty()) {
return true;
}
boolean northsouth = retval.contains(EnumDirection.NORTH) || retval.contains(EnumDirection.SOUTH);
boolean eastwest = retval.contains(EnumDirection.EAST) || retval.contains(EnumDirection.WEST);
if (northsouth) {
retval.remove(EnumDirection.EAST);
retval.remove(EnumDirection.WEST);
}
if (eastwest) {
retval.remove(EnumDirection.NORTH);
retval.remove(EnumDirection.SOUTH);
}
return retval.contains(side);
}
/**
* Adds all surrounding positions to a set.
* This is the neighbor blocks, as well as their neighbors
*
* @param pos
* @param set
*/
private void addAllSurroundingBlocks(BlockPosition pos, Set<BlockPosition> set) {
for (BaseBlockPosition vect : surroundingBlocksOffset) {
set.add(pos.a(vect));
}
}
/**
* Sets the block state of a wire with a new power level and marks for updates
*
* @author panda
*
* @param worldIn World
* @param pos Position at which the state needs to be set
* @param state Old state
* @param power Power it should get set to
*/
private void setWireState(World worldIn, BlockPosition pos, IBlockData state, int power) {
state = state.set(POWER, power);
worldIn.setTypeAndData(pos, state, 2);
updatedRedstoneWire.add(pos);
}
public void onPlace(World world, BlockPosition blockposition, IBlockData iblockdata) {
this.updateSurroundingRedstone(world, blockposition, world.getType(blockposition));
for (EnumDirection enumdirection : EnumDirection.values()) {
world.applyPhysics(blockposition.shift(enumdirection), this);
}
}
public void remove(World world, BlockPosition blockposition, IBlockData iblockdata) {
for (EnumDirection enumdirection : EnumDirection.values()) {
world.applyPhysics(blockposition.shift(enumdirection), this);
}
this.updateSurroundingRedstone(world, blockposition, world.getType(blockposition));
}
public void doPhysics(World world, BlockPosition blockposition, IBlockData iblockdata, Block block) {
if (this.canPlace(world, blockposition)) {
this.updateSurroundingRedstone(world, blockposition, iblockdata);
} else {
this.b(world, blockposition, iblockdata, 0);
world.setAir(blockposition);
}
}
protected final int getPower(IBlockData state, int power) {
if (state.getBlock() != Blocks.REDSTONE_WIRE) {
return power;
}
int j = state.get(BlockRedstoneWire.POWER);
return Math.max(j, power);
}
public int a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, EnumDirection enumdirection) {
if (!this.canProvidePower) {
return 0;
} else {
int i = iblockdata.get(BlockRedstoneWire.POWER);
if (i == 0) { // md_5 change? This isn't in the original.
return 0;
} else if (enumdirection == EnumDirection.UP) {
return i;
} else {
return this.canSidePower((World) iblockaccess, blockposition, enumdirection) ? i : 0;
}
}
}
private boolean isPowerSourceAt(IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
BlockPosition blockpos = blockposition.shift(enumdirection);
IBlockData iblockdata = iblockaccess.getType(blockpos);
Block block = iblockdata.getBlock();
boolean flag = block.isOccluding();
boolean flag1 = iblockaccess.getType(blockposition.up()).getBlock().isOccluding();
return !flag1 && flag && e(iblockaccess, blockpos.up()) || (a(iblockdata, enumdirection) || (block == Blocks.POWERED_REPEATER && iblockdata.get(BlockDiodeAbstract.FACING) == enumdirection || !flag && e(iblockaccess, blockpos.down())));
}
public static class ReflectUtil {
public static <T> T getOfT(Object obj, Class<T> type) {
for (Field field : obj.getClass().getDeclaredFields()) {
if (type.equals(field.getType())) {
return get(obj, field, type);
}
}
return null;
}
public static <T> T get(Object obj, Field field, Class<T> type) {
try {
field.setAccessible(true);
return type.cast(field.get(obj));
} catch (ReflectiveOperationException ex) {
ex.printStackTrace();
return null;
}
}
}
}

View File

@ -1,5 +1,6 @@
package com.elevatemc.spigot.threading; package com.elevatemc.spigot.threading;
import com.elevatemc.spigot.config.eSpigotConfig;
import com.elevatemc.spigot.pathsearch.PathSearchThrottlerThread; import com.elevatemc.spigot.pathsearch.PathSearchThrottlerThread;
import com.elevatemc.spigot.pathsearch.jobs.PathSearchJob; import com.elevatemc.spigot.pathsearch.jobs.PathSearchJob;
import net.minecraft.server.NBTCompressedStreamTools; import net.minecraft.server.NBTCompressedStreamTools;
@ -35,6 +36,8 @@ public class ThreadingManager {
private TaskQueueWorker nbtFiles; private TaskQueueWorker nbtFiles;
private TaskQueueWorker headConversions; private TaskQueueWorker headConversions;
public static ThreadPoolExecutor asyncExplosionsExecutor;
public ThreadingManager() { public ThreadingManager() {
instance = this; instance = this;
this.pathSearchThrottler = new PathSearchThrottlerThread(2); this.pathSearchThrottler = new PathSearchThrottlerThread(2);
@ -44,12 +47,18 @@ public class ThreadingManager {
this.cachedThreadPool = Executors.newCachedThreadPool(this.cachedThreadPoolFactory); this.cachedThreadPool = Executors.newCachedThreadPool(this.cachedThreadPoolFactory);
this.nbtFiles = this.createTaskQueueWorker(); this.nbtFiles = this.createTaskQueueWorker();
this.headConversions = this.createTaskQueueWorker(); this.headConversions = this.createTaskQueueWorker();
if (eSpigotConfig.fixedPoolForTNT)
asyncExplosionsExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(eSpigotConfig.fixedPoolSizeForTNT);
else
asyncExplosionsExecutor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
} }
public void shutdown() { public void shutdown() {
this.pathSearchThrottler.shutdown(); this.pathSearchThrottler.shutdown();
this.timerService.shutdown(); this.timerService.shutdown();
this.cachedThreadPool.shutdown(); this.cachedThreadPool.shutdown();
this.asyncExplosionsExecutor.shutdown();
while((this.nbtFiles.isActive()) && !this.cachedThreadPool.isTerminated()) { while((this.nbtFiles.isActive()) && !this.cachedThreadPool.isTerminated()) {
try { try {
this.cachedThreadPool.awaitTermination(10, TimeUnit.SECONDS); this.cachedThreadPool.awaitTermination(10, TimeUnit.SECONDS);

View File

@ -0,0 +1,202 @@
package com.elevatemc.spigot.util;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.AbstractReferenceList;
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
import it.unimi.dsi.fastutil.objects.ObjectSpliterator;
import java.util.Arrays;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* list with O(1) remove & contains
* @author Spottedleaf
*/
@SuppressWarnings("unchecked")
public final class ObjectMapList<T> extends AbstractReferenceList<T> implements Set<T> {
protected final Int2IntOpenHashMap objectToIndex;
protected static final Object[] EMPTY_LIST = new Object[0];
protected T[] elements = (T[]) EMPTY_LIST;
protected int count;
public ObjectMapList() {
this(2, 0.8f);
}
public ObjectMapList(int expectedSize, float loadFactor) {
this.objectToIndex = new Int2IntOpenHashMap(expectedSize, loadFactor);
this.objectToIndex.defaultReturnValue(Integer.MIN_VALUE);
}
@Override
public int size() {
return this.count;
}
@Override
public int indexOf(Object object) {
return this.objectToIndex.get(object.hashCode());
}
@Override
public int lastIndexOf(Object object) {
return super.indexOf(object);
}
@Override
public boolean remove(final Object object) {
final int index = this.objectToIndex.remove(object.hashCode());
if (index == Integer.MIN_VALUE) {
return false;
}
// move the obj at the end to this index
final int endIndex = --this.count;
final T end = this.elements[endIndex];
if (index != endIndex) {
// not empty after this call
this.objectToIndex.put(end.hashCode(), index); // update index
}
this.elements[index] = end;
this.elements[endIndex] = null;
return true;
}
@Override
public boolean add(final T object) {
final int count = this.count;
final int currIndex = this.objectToIndex.putIfAbsent(object.hashCode(), count);
if (currIndex != Integer.MIN_VALUE) {
return false; // already in this list
}
T[] list = this.elements;
if (list.length == count) {
// resize required
list = this.elements = Arrays.copyOf(list, (int)Math.max(4L, (long) count << 1)); // overflow results in negative
}
list[count] = object;
this.count = count + 1;
return true;
}
@Override
public void add(final int index, final T object) {
final int currIndex = this.objectToIndex.putIfAbsent(object.hashCode(), index);
if (currIndex != Integer.MIN_VALUE) {
return; // already in this list
}
int count = this.count;
T[] list = this.elements;
if (list.length == count) {
// resize required
list = this.elements = Arrays.copyOf(list, (int) Math.max(4L, (long) count << 1)); // overflow results in negative
}
System.arraycopy(list, index, list, index + 1, count - index);
list[index] = object;
this.count = count + 1;
}
@Override
public T get(int index) {
return this.elements[index];
}
@Override
public boolean isEmpty() {
return this.count == 0;
}
public T[] getRawData() {
return this.elements;
}
@Override
public void clear() {
this.objectToIndex.clear();
Arrays.fill(this.elements, 0, this.count, null);
this.count = 0;
}
@Override
public Object[] toArray() {
return Arrays.copyOf(this.elements, this.count);
}
@Override
public ObjectSpliterator<T> spliterator() {
return super.spliterator();
}
@Override
public ObjectListIterator<T> iterator() {
return new Iterator(0);
}
private class Iterator implements ObjectListIterator<T> {
T lastRet;
int current;
Iterator(int index) {
current = index;
}
@Override
public int nextIndex() {
return this.current + 1;
}
@Override
public int previousIndex() {
return this.current - 1;
}
@Override
public boolean hasNext() {
return this.current < ObjectMapList.this.count;
}
@Override
public boolean hasPrevious() {
return this.current > 0;
}
@Override
public T next() {
if (this.current >= ObjectMapList.this.count) {
throw new NoSuchElementException();
}
return this.lastRet = ObjectMapList.this.elements[this.current++];
}
@Override
public T previous() {
if (this.current < 0) {
throw new NoSuchElementException();
}
return this.lastRet = ObjectMapList.this.elements[--this.current];
}
@Override
public void remove() {
final T lastRet = this.lastRet;
if (lastRet == null) {
throw new IllegalStateException();
}
this.lastRet = null;
ObjectMapList.this.remove(lastRet);
--this.current;
}
}
}

View File

@ -0,0 +1,131 @@
package com.elevatemc.spigot.util;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Iterators;
import com.google.common.collect.Multimap;
import it.unimi.dsi.fastutil.objects.Object2LongMap;
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
import net.minecraft.server.BlockJukeBox.TileEntityRecordPlayer;
import net.minecraft.server.*;
import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* Optimized world tile entity list implementation, provides
* an iterator of tile entities that need to be ticked based
* on the world time to reduce needed iteration/checks.
* @author Rastrian
*/
public final class OptimizedWorldTileEntitySet extends AbstractSet<TileEntity> {
/**
* Map of tile classes with modified tick intervals.
*/
private static final Object2LongMap<Class<? extends TileEntity>> CUSTOM_TICK_INTERVALS =
new Object2LongOpenHashMap<Class<? extends TileEntity>>() {{
// Entities with empty tick# methods.
this.put(TileEntityCommand.class, -1L);
this.put(TileEntityComparator.class, -1L);
this.put(TileEntityDispenser.class, -1L);
this.put(TileEntityDropper.class, -1L);
this.put(TileEntityEnderPortal.class, -1L);
this.put(TileEntityFlowerPot.class, -1L);
this.put(TileEntityNote.class, -1L);
this.put(TileEntityRecordPlayer.class, -1L);
this.put(TileEntitySign.class, -1L);
this.put(TileEntitySkull.class, -1L);
// Entities that we have modified to have empty tick# methods.
this.put(TileEntityEnderChest.class, -1L);
this.put(TileEntityChest.class, -1L);
// Entities with vanilla controlled tick id checks inside
// the tick# methods, helps to schedule only when required.
this.put(TileEntityBeacon.class, 80L);
this.put(TileEntityLightDetector.class, 20L);
// Entities that do a scanPlayer# lookup, helps to slow down.
this.put(TileEntityEnchantTable.class, 20L);
this.put(TileEntityMobSpawner.class, 20L);
}};
/**
* Multimap of all registered tile entities.
*/
private final Multimap<Class<? extends TileEntity>, TileEntity> registeredTiles = HashMultimap.create();
@Override
public int size() {
return this.registeredTiles.size();
}
@Override
public boolean add(TileEntity tile) {
if (tile == null) {
return false;
}
if (this.registeredTiles.containsValue(tile)) {
return false;
}
return this.registeredTiles.put(tile.getClass(), tile);
}
@Override
public boolean remove(Object object) {
if (object == null) {
return false;
}
return this.registeredTiles.remove(object.getClass(), object);
}
@Override
public boolean contains(Object object) {
if (object == null) {
return false;
}
return this.registeredTiles.containsEntry(object.getClass(), object);
}
@Override
public void clear() {
this.registeredTiles.clear();
}
@Override
public @NotNull Iterator<TileEntity> iterator() {
return this.registeredTiles.values().iterator();
}
public Iterator<TileEntity> tickIterator(long worldTime) {
LinkedList<Iterator<TileEntity>> tileIterators = new LinkedList<>();
for (Class<? extends TileEntity> tileClassToTick : this.getTileClassesToTick(worldTime)) {
Collection<TileEntity> tileBucket = this.registeredTiles.get(tileClassToTick);
if (tileBucket != null) {
tileIterators.add(tileBucket.iterator());
}
}
return Iterators.concat(tileIterators.iterator());
}
private List<Class<? extends TileEntity>> getTileClassesToTick(long worldTime) {
List<Class<? extends TileEntity>> tilesToTick = new LinkedList<>();
for (Class<? extends TileEntity> registeredTileClass : this.registeredTiles.keySet()) {
long customTickInterval = OptimizedWorldTileEntitySet.CUSTOM_TICK_INTERVALS.getLong(registeredTileClass);
if (customTickInterval != 0) { // Troves non-existent value is 0.
if (customTickInterval > 0 && (worldTime == 0 || worldTime % customTickInterval == 0)) {
tilesToTick.add(registeredTileClass);
}
continue;
}
tilesToTick.add(registeredTileClass);
}
return tilesToTick;
}
}

View File

@ -0,0 +1,111 @@
package com.elevatemc.spigot.visuals;
import net.minecraft.server.*;
import java.util.List;
/*
* This is a custom entity tracker made for the cannoning entities tnt and sand.
* The goal behind this is to reduce packets and logic without hiding entities.
* It may not completely replicate the original behavior, but it should make up
* for that with it's advantages.
* Source: IonSpigot
*/
public class CannonTrackerEntry extends EntityTrackerEntry {
private boolean movingX;
private boolean movingY;
private boolean movingZ;
private double updateX;
private double updateY;
private double updateZ;
public CannonTrackerEntry(Entity entity, int i, int j, boolean flag) {
super(entity, i, j, flag);
this.movingX = entity.motX != 0.0;
this.movingY = true;
this.movingZ = entity.motZ != 0.0;
this.updateX = entity.locX;
this.updateY = entity.locY;
this.updateZ = entity.locZ;
}
@Override
public void track(List<EntityHuman> list) {
boolean motionX = this.tracker.motX != 0.0;
boolean motionY = this.tracker.motY != 0.0;
boolean motionZ = this.tracker.motZ != 0.0;
// This tracked entities motion has changed or an explosion has occurred, update it!
if (!this.tracker.ai && motionX == movingX && motionY == movingY && motionZ == movingZ) {
return;
}
// This entity has moved 4 blocks since the last update, search for players
if (this.tracker.e(updateX, updateY, updateZ) > 16.0D) {
this.scanPlayers(list);
this.updateX = this.tracker.locX;
this.updateY = this.tracker.locY;
this.updateZ = this.tracker.locZ;
}
// Update nearby players, only resynchronise when motion is updated
if (motionX || motionY || motionZ) {
this.broadcastUpdate();
}
// Keep what of which axis the entity is moving on
this.tracker.ai = false;
this.movingX = motionX;
this.movingY = motionY;
this.movingZ = motionZ;
}
private void broadcastUpdate() {
DataWatcher datawatcher = this.tracker.getDataWatcher();
if (datawatcher.a()) {
this.broadcastIncludingSelf(new PacketPlayOutEntityMetadata(this.tracker.getId(), datawatcher, false));
}
// Only update location on movement
if (this.tracker.lastX != this.tracker.locX || this.tracker.lastY != this.tracker.locY || this.tracker.lastZ != this.tracker.locZ) {
this.broadcast(new PacketPlayOutEntityTeleport(this.tracker));
}
this.broadcast(new PacketPlayOutEntityVelocity(this.tracker));
}
@Override
public void updatePlayer(EntityPlayer entityplayer) {
// Check configurable distance as a cube then visible distance.
if (this.c(entityplayer) && this.tracker.h(entityplayer) < 4096.0D) {
if (this.trackedPlayers.contains(entityplayer) || (!this.e(entityplayer) && !this.tracker.attachedToPlayer)) {
return;
}
entityplayer.removeQueue.remove(Integer.valueOf(this.tracker.getId()));
this.trackedPlayerMap.put(entityplayer, true); // Paper
Packet packet = this.c(); // IonSpigot
if (packet == null) return; // IonSpigot - If it's null don't update the client!
entityplayer.playerConnection.sendPacket(packet);
if (this.tracker.getCustomNameVisible()) {
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityMetadata(this.tracker.getId(), this.tracker.getDataWatcher(), true));
}
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityVelocity(this.tracker.getId(), this.tracker.motX, this.tracker.motY, this.tracker.motZ));
if (this.tracker.vehicle != null) {
entityplayer.playerConnection.sendPacket(new PacketPlayOutAttachEntity(0, this.tracker, this.tracker.vehicle));
}
} else if (this.trackedPlayers.contains(entityplayer)) {
this.trackedPlayers.remove(entityplayer);
entityplayer.d(this.tracker);
}
}
}

View File

@ -1,6 +1,8 @@
package net.minecraft.server; package net.minecraft.server;
import com.elevatemc.spigot.config.eSpigotConfig;
import com.elevatemc.spigot.event.BlockDropItemsEvent; import com.elevatemc.spigot.event.BlockDropItemsEvent;
import com.elevatemc.spigot.redstone.PandaRedstoneWire;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.entity.CraftItem; import org.bukkit.craftbukkit.entity.CraftItem;
@ -805,7 +807,7 @@ public class Block {
a(52, "mob_spawner", (new BlockMobSpawner()).c(5.0F).a(Block.j).c("mobSpawner").K()); a(52, "mob_spawner", (new BlockMobSpawner()).c(5.0F).a(Block.j).c("mobSpawner").K());
a(53, "oak_stairs", (new BlockStairs(block1.getBlockData().set(BlockWood.VARIANT, BlockWood.EnumLogVariant.OAK))).c("stairsWood")); a(53, "oak_stairs", (new BlockStairs(block1.getBlockData().set(BlockWood.VARIANT, BlockWood.EnumLogVariant.OAK))).c("stairsWood"));
a(54, "chest", (new BlockChest(0)).c(2.5F).a(Block.f).c("chest")); a(54, "chest", (new BlockChest(0)).c(2.5F).a(Block.f).c("chest"));
a(55, "redstone_wire", (new BlockRedstoneWire()).c(0.0F).a(Block.e).c("redstoneDust").K()); a(55, "redstone_wire", (eSpigotConfig.pandaWire ? new PandaRedstoneWire() : new BlockRedstoneWire()).c(0.0F).a(Block.e).c("redstoneDust").K());
a(56, "diamond_ore", (new BlockOre()).c(3.0F).b(5.0F).a(Block.i).c("oreDiamond")); a(56, "diamond_ore", (new BlockOre()).c(3.0F).b(5.0F).a(Block.i).c("oreDiamond"));
a(57, "diamond_block", (new Block(Material.ORE, MaterialMapColor.G)).c(5.0F).b(10.0F).a(Block.j).c("blockDiamond").a(CreativeModeTab.b)); a(57, "diamond_block", (new Block(Material.ORE, MaterialMapColor.G)).c(5.0F).b(10.0F).a(Block.j).c("blockDiamond").a(CreativeModeTab.b));
a(58, "crafting_table", (new BlockWorkbench()).c(2.5F).a(Block.f).c("workbench")); a(58, "crafting_table", (new BlockWorkbench()).c(2.5F).a(Block.f).c("workbench"));

View File

@ -34,7 +34,7 @@ public class BlockFalling extends Block {
if (canFall(world, blockposition.down()) && blockposition.getY() >= 0) { if (canFall(world, blockposition.down()) && blockposition.getY() >= 0) {
byte b0 = 32; byte b0 = 32;
if (!BlockFalling.instaFall && world.areChunksLoadedBetween(blockposition.a(-b0, -b0, -b0), blockposition.a(b0, b0, b0))) { if (world.paperSpigotConfig.fixSandUnloading || !BlockFalling.instaFall && world.areChunksLoadedBetween(blockposition.a(-b0, -b0, -b0), blockposition.a(b0, b0, b0))) {
if (!world.isClientSide) { if (!world.isClientSide) {
// PaperSpigot start - Add FallingBlock source location API // PaperSpigot start - Add FallingBlock source location API
org.bukkit.Location loc = new org.bukkit.Location(world.getWorld(), (float) blockposition.getX() + 0.5F, blockposition.getY(), (float) blockposition.getZ() + 0.5F); org.bukkit.Location loc = new org.bukkit.Location(world.getWorld(), (float) blockposition.getX() + 0.5F, blockposition.getY(), (float) blockposition.getZ() + 0.5F);

View File

@ -28,6 +28,7 @@ public class BlockFlowing extends BlockFluids {
org.bukkit.Server server = world.getServer(); org.bukkit.Server server = world.getServer();
org.bukkit.block.Block source = bworld == null ? null : bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); org.bukkit.block.Block source = bworld == null ? null : bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
// CraftBukkit end // CraftBukkit end
BlockPosition pos = blockposition.down(); // IonSpigot - Cache Below Position
int i = iblockdata.get(BlockFlowing.LEVEL); int i = iblockdata.get(BlockFlowing.LEVEL);
byte b0 = 1; byte b0 = 1;
@ -35,7 +36,7 @@ public class BlockFlowing extends BlockFluids {
b0 = 2; b0 = 2;
} }
int j = this.getFlowSpeed(world, blockposition); // PaperSpigot // int j = this.getFlowSpeed(world, blockposition); // PaperSpigot
int k; int k;
if (i > 0) { if (i > 0) {
@ -43,20 +44,30 @@ public class BlockFlowing extends BlockFluids {
this.a = 0; this.a = 0;
EnumDirection enumdirection; // IonSpigot start - Optimise Fluids
int temp = this.e(world, blockposition.up());
int i1 = b0;
if (temp < 8) {
EnumDirection enumdirection;
for (Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); iterator.hasNext(); l = this.a(world, blockposition.shift(enumdirection), l)) { for (Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); iterator.hasNext(); l = this.a(world, blockposition.shift(enumdirection), l)) {
enumdirection = (EnumDirection) iterator.next(); enumdirection = (EnumDirection) iterator.next();
}
i1 = l + b0;
if (i1 >= 8 || l < 0) {
i1 = -1;
}
} else if (temp == 8 && world.getType(pos) == Blocks.AIR.getBlockData()) {
// TODO: Look into adding an option to make this 2
this.f(world, blockposition, iblockdata);
world.setTypeAndData(pos, iblockdata, 3);
return;
} }
int i1 = l + b0; if (temp >= 0) {
k = temp;
if (i1 >= 8 || l < 0) {
i1 = -1;
}
if (this.e(world, blockposition.up()) >= 0) {
k = this.e(world, blockposition.up());
if (k >= 8) { if (k >= 8) {
i1 = k; i1 = k;
} else { } else {
@ -65,7 +76,7 @@ public class BlockFlowing extends BlockFluids {
} }
if (this.a >= 2 && this.material == Material.WATER) { if (this.a >= 2 && this.material == Material.WATER) {
IBlockData iblockdata1 = world.getType(blockposition.down()); IBlockData iblockdata1 = world.getType(pos); // IonSpigot - Cache Below Position
if (iblockdata1.getBlock().getMaterial().isBuildable()) { if (iblockdata1.getBlock().getMaterial().isBuildable()) {
i1 = 0; i1 = 0;
@ -74,9 +85,13 @@ public class BlockFlowing extends BlockFluids {
} }
} }
if (!world.paperSpigotConfig.fastDrainLava && this.material == Material.LAVA && i < 8 && i1 < 8 && i1 > i && random.nextInt(4) != 0) { // PaperSpigot // IonSpigot start - Unused logic
j *= 4; /*
} if (!world.paperSpigotConfig.fastDrainLava && this.material == Material.LAVA && i < 8 && i1 < 8 && i1 > i && random.nextInt(4) != 0) { // PaperSpigot
j *= 4;
}
*/
// IonSpigot end
if (i1 == i) { if (i1 == i) {
this.f(world, blockposition, iblockdata); this.f(world, blockposition, iblockdata);
@ -85,6 +100,7 @@ public class BlockFlowing extends BlockFluids {
if (i1 < 0 || canFastDrain(world, blockposition)) { // PaperSpigot - Fast draining if (i1 < 0 || canFastDrain(world, blockposition)) { // PaperSpigot - Fast draining
world.setAir(blockposition); world.setAir(blockposition);
} else { } else {
int j = this.getFlowSpeed(world, blockposition); // PaperSpigot // IonSpigot - From above
iblockdata = iblockdata.set(BlockFlowing.LEVEL, i1); iblockdata = iblockdata.set(BlockFlowing.LEVEL, i1);
world.setTypeAndData(blockposition, iblockdata, 2); world.setTypeAndData(blockposition, iblockdata, 2);
world.a(blockposition, this, j); world.a(blockposition, this, j);
@ -103,9 +119,10 @@ public class BlockFlowing extends BlockFluids {
} }
if (world.getType(blockposition).getBlock().getMaterial() != material) return; // PaperSpigot - Stop updating flowing block if material has changed if (world.getType(blockposition).getBlock().getMaterial() != material) return; // PaperSpigot - Stop updating flowing block if material has changed
IBlockData iblockdata2 = world.getType(blockposition.down()); // IonSpigot start - Cache Below Position
IBlockData iblockdata2 = world.getType(pos);
if (this.h(world, blockposition.down(), iblockdata2)) { if (this.h(world, pos, iblockdata2)) {
// CraftBukkit start - Send "down" to the server // CraftBukkit start - Send "down" to the server
if (!canFlowTo(world, source, BlockFace.DOWN)) { return; } // Paper if (!canFlowTo(world, source, BlockFace.DOWN)) { return; } // Paper
BlockFromToEvent event = new BlockFromToEvent(source, BlockFace.DOWN); BlockFromToEvent event = new BlockFromToEvent(source, BlockFace.DOWN);
@ -113,20 +130,21 @@ public class BlockFlowing extends BlockFluids {
server.getPluginManager().callEvent(event); server.getPluginManager().callEvent(event);
} }
if (!event.isCancelled()) { if (!event.isCancelled()) {
if (this.material == Material.LAVA && world.getType(blockposition.down()).getBlock().getMaterial() == Material.WATER) { if (this.material == Material.LAVA && world.getType(pos).getBlock().getMaterial() == Material.WATER) {
world.setTypeUpdate(blockposition.down(), Blocks.STONE.getBlockData()); world.setTypeUpdate(pos, Blocks.STONE.getBlockData());
this.fizz(world, blockposition.down()); this.fizz(world, pos);
return; return;
} }
if (i >= 8) { if (i >= 8) {
this.flow(world, blockposition.down(), iblockdata2, i); this.flow(world, pos, iblockdata2, i);
} else { } else {
this.flow(world, blockposition.down(), iblockdata2, i + 8); this.flow(world, pos, iblockdata2, i + 8);
} }
} }
// CraftBukkit end // CraftBukkit end
} else if (i >= 0 && (i == 0 || this.g(world, blockposition.down(), iblockdata2))) { } else if (i >= 0 && (i == 0 || this.g(world, pos, iblockdata2))) {
// IonSpigot end
Set set = this.f(world, blockposition); Set set = this.f(world, blockposition);
k = i + b0; k = i + b0;
@ -150,7 +168,7 @@ public class BlockFlowing extends BlockFluids {
} }
if (!event.isCancelled()) { if (!event.isCancelled()) {
this.flow(world, blockposition.shift(enumdirection1), world.getType(blockposition.shift(enumdirection1)), k); this.flow(world, pos = blockposition.shift(enumdirection1), world.getType(pos), k); // IonSpigot - Don't shift twice
} }
} }
// CraftBukkit end // CraftBukkit end
@ -188,7 +206,7 @@ public class BlockFlowing extends BlockFluids {
BlockPosition blockposition1 = blockposition.shift(enumdirection1); BlockPosition blockposition1 = blockposition.shift(enumdirection1);
IBlockData iblockdata = world.getType(blockposition1); IBlockData iblockdata = world.getType(blockposition1);
if (!this.g(world, blockposition1, iblockdata) && (iblockdata.getBlock().getMaterial() != this.material || iblockdata.get(BlockFlowing.LEVEL) > 0)) { if (!this.g(iblockdata.getBlock()) && (iblockdata.getBlock().getMaterial() != this.material || iblockdata.get(BlockFlowing.LEVEL) > 0)) { // IonSpigot - Optimise Fluids
if (!this.g(world, blockposition1.down(), iblockdata)) { if (!this.g(world, blockposition1.down(), iblockdata)) {
return i; return i;
} }
@ -215,11 +233,11 @@ public class BlockFlowing extends BlockFluids {
BlockPosition blockposition1 = blockposition.shift(enumdirection); BlockPosition blockposition1 = blockposition.shift(enumdirection);
IBlockData iblockdata = world.getType(blockposition1); IBlockData iblockdata = world.getType(blockposition1);
if (!this.g(world, blockposition1, iblockdata) && (iblockdata.getBlock().getMaterial() != this.material || iblockdata.get(BlockFlowing.LEVEL) > 0)) { if (!this.g(iblockdata.getBlock()) && (iblockdata.getBlock().getMaterial() != this.material || iblockdata.get(BlockFlowing.LEVEL) > 0)) { // IonSpigot - Optimise Fluids
int j; int j;
if (this.g(world, blockposition1.down(), world.getType(blockposition1.down()))) { if (this.g(world, blockposition1.down(), iblockdata)) { // IonSpigot - Unused check
j = this.a(world, blockposition1, 1, enumdirection.opposite()); j = this.a(world, blockposition1, 4, enumdirection.opposite()); // IonSpigot - Optimise Fluids
} else { } else {
j = 0; j = 0;
} }
@ -240,7 +258,11 @@ public class BlockFlowing extends BlockFluids {
private boolean g(World world, BlockPosition blockposition, IBlockData iblockdata) { private boolean g(World world, BlockPosition blockposition, IBlockData iblockdata) {
Block block = world.getType(blockposition).getBlock(); Block block = world.getType(blockposition).getBlock();
// IonSpigot start - Optimise Fluids
return this.g(block);
}
private boolean g(Block block) {
// IonSpigot end
return block instanceof BlockDoor || block == Blocks.STANDING_SIGN || block == Blocks.LADDER || block == Blocks.REEDS || (block.material == Material.PORTAL || block.material.isSolid()); return block instanceof BlockDoor || block == Blocks.STANDING_SIGN || block == Blocks.LADDER || block == Blocks.REEDS || (block.material == Material.PORTAL || block.material.isSolid());
} }
@ -265,7 +287,7 @@ public class BlockFlowing extends BlockFluids {
private boolean h(World world, BlockPosition blockposition, IBlockData iblockdata) { private boolean h(World world, BlockPosition blockposition, IBlockData iblockdata) {
Material material = iblockdata.getBlock().getMaterial(); Material material = iblockdata.getBlock().getMaterial();
return material != this.material && material != Material.LAVA && !this.g(world, blockposition, iblockdata); return material != this.material && material != Material.LAVA && !this.g(iblockdata.getBlock()); // IonSpigot - Optimise Fluids
} }
public void onPlace(World world, BlockPosition blockposition, IBlockData iblockdata) { public void onPlace(World world, BlockPosition blockposition, IBlockData iblockdata) {
@ -296,7 +318,16 @@ public class BlockFlowing extends BlockFluids {
* PaperSpigot - Data check method for fast draining * PaperSpigot - Data check method for fast draining
*/ */
public int getData(World world, BlockPosition position) { public int getData(World world, BlockPosition position) {
int data = this.e(world, position); // IonSpigot start - Optimise Draining
return getData(world.getType(position));
}
public boolean checkData(World world, BlockPosition position, Material material, int data) {
IBlockData state = world.getType(position);
return state.getBlock().getMaterial() == material && getData(state) < data;
}
public int getData(IBlockData iblockdata) {
int data = this.e(iblockdata);
// IonSpigot end
return data < 8 ? data : 0; return data < 8 ? data : 0;
} }
@ -311,13 +342,15 @@ public class BlockFlowing extends BlockFluids {
result = true; result = true;
if (getData(world, position.down()) < 0) { if (getData(world, position.down()) < 0) {
result = false; result = false;
} else if (world.getType(position.north()).getBlock().getMaterial() == Material.WATER && getData(world, position.north()) < data) { // IonSpigot start - Optimise Draining
} else if (checkData(world, position.north(), Material.WATER, data)) {
result = false; result = false;
} else if (world.getType(position.south()).getBlock().getMaterial() == Material.WATER && getData(world, position.south()) < data) { } else if (checkData(world, position.south(), Material.WATER, data)) {
result = false; result = false;
} else if (world.getType(position.west()).getBlock().getMaterial() == Material.WATER && getData(world, position.west()) < data) { } else if (checkData(world, position.west(), Material.WATER, data)) {
result = false; result = false;
} else if (world.getType(position.east()).getBlock().getMaterial() == Material.WATER && getData(world, position.east()) < data) { } else if (checkData(world, position.east(), Material.WATER, data)) {
// Ionspigot end
result = false; result = false;
} }
} }
@ -326,13 +359,15 @@ public class BlockFlowing extends BlockFluids {
result = true; result = true;
if (getData(world, position.down()) < 0 || world.getType(position.up()).getBlock().getMaterial() != Material.AIR) { if (getData(world, position.down()) < 0 || world.getType(position.up()).getBlock().getMaterial() != Material.AIR) {
result = false; result = false;
} else if (world.getType(position.north()).getBlock().getMaterial() == Material.LAVA && getData(world, position.north()) < data) { // IonSpigot start - Optimise Draining
} else if (checkData(world, position.north(), Material.LAVA, data)) {
result = false; result = false;
} else if (world.getType(position.south()).getBlock().getMaterial() == Material.LAVA && getData(world, position.south()) < data) { } else if (checkData(world, position.south(), Material.LAVA, data)) {
result = false; result = false;
} else if (world.getType(position.west()).getBlock().getMaterial() == Material.LAVA && getData(world, position.west()) < data) { } else if (checkData(world, position.west(), Material.LAVA, data)) {
result = false; result = false;
} else if (world.getType(position.east()).getBlock().getMaterial() == Material.LAVA && getData(world, position.east()) < data) { } else if (checkData(world, position.east(), Material.LAVA, data)) {
// IonSpigot end
result = false; result = false;
} }
} }

View File

@ -30,7 +30,12 @@ public abstract class BlockFluids extends Block {
} }
protected int e(IBlockAccess iblockaccess, BlockPosition blockposition) { protected int e(IBlockAccess iblockaccess, BlockPosition blockposition) {
return iblockaccess.getType(blockposition).getBlock().getMaterial() == this.material ? iblockaccess.getType(blockposition).get(BlockFluids.LEVEL) : -1; // IonSpigot start - Optimise Draining
return e(iblockaccess.getType(blockposition));
}
protected int e(IBlockData iblockdata) {
return iblockdata.getBlock().getMaterial() == this.material ? iblockdata.get(BlockFluids.LEVEL) : -1;
// IonSpigot end
} }
protected int f(IBlockAccess iblockaccess, BlockPosition blockposition) { protected int f(IBlockAccess iblockaccess, BlockPosition blockposition) {
@ -172,10 +177,13 @@ public abstract class BlockFluids extends Block {
world.makeSound(d0 + 0.5D, d1 + 0.5D, d2 + 0.5D, "random.fizz", 0.5F, 2.6F + (world.random.nextFloat() - world.random.nextFloat()) * 0.8F); world.makeSound(d0 + 0.5D, d1 + 0.5D, d2 + 0.5D, "random.fizz", 0.5F, 2.6F + (world.random.nextFloat() - world.random.nextFloat()) * 0.8F);
for (int i = 0; i < 8; ++i) { // IonSpigot start - Remove unused code
world.addParticle(EnumParticle.SMOKE_LARGE, d0 + Math.random(), d1 + 1.2D, d2 + Math.random(), 0.0D, 0.0D, 0.0D, Constants.EMPTY_ARRAY); /*
} for (int i = 0; i < 8; ++i) {
world.addParticle(EnumParticle.SMOKE_LARGE, d0 + Math.random(), d1 + 1.2D, d2 + Math.random(), 0.0D, 0.0D, 0.0D, new int[0]);
}
*/
// IonSpigot end
} }
public IBlockData fromLegacyData(int i) { public IBlockData fromLegacyData(int i) {

View File

@ -28,19 +28,23 @@ public class BlockHopper extends BlockContainer {
} }
public void a(World world, BlockPosition blockposition, IBlockData iblockdata, AxisAlignedBB axisalignedbb, List<AxisAlignedBB> list, Entity entity) { public void a(World world, BlockPosition blockposition, IBlockData iblockdata, AxisAlignedBB axisalignedbb, List<AxisAlignedBB> list, Entity entity) {
// North side
this.a(0.0F, 0.625F, 0.125F, 1.0F, 1.0F, 0.0F);
super.a(world, blockposition, iblockdata, axisalignedbb, list, entity);
// South side
this.a(0.0F, 0.625F, 0.875F, 1.0F, 1.0F, 1.0F);
super.a(world, blockposition, iblockdata, axisalignedbb, list, entity);
// West side
this.a(0.125F, 0.625F, 0.0F, 0.0F, 1.0F, 1.0F);
super.a(world, blockposition, iblockdata, axisalignedbb, list, entity);
// East side
this.a(0.875F, 0.625F, 0.0F, 1.0F, 1.0F, 1.0F);
super.a(world, blockposition, iblockdata, axisalignedbb, list, entity);
// Bottom part
this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 1.0F); this.a(0.0F, 0.0F, 0.0F, 1.0F, 0.625F, 1.0F);
super.a(world, blockposition, iblockdata, axisalignedbb, list, entity); super.a(world, blockposition, iblockdata, axisalignedbb, list, entity);
float f = 0.125F; // Reset
this.updateShape(world, blockposition);
this.a(0.0F, 0.0F, 0.0F, f, 1.0F, 1.0F);
super.a(world, blockposition, iblockdata, axisalignedbb, list, entity);
this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, f);
super.a(world, blockposition, iblockdata, axisalignedbb, list, entity);
this.a(1.0F - f, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
super.a(world, blockposition, iblockdata, axisalignedbb, list, entity);
this.a(0.0F, 0.0F, 1.0F - f, 1.0F, 1.0F, 1.0F);
super.a(world, blockposition, iblockdata, axisalignedbb, list, entity);
this.a(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
} }
public IBlockData getPlacedState(World world, BlockPosition blockposition, EnumDirection enumdirection, float f, float f1, float f2, int i, EntityLiving entityliving) { public IBlockData getPlacedState(World world, BlockPosition blockposition, EnumDirection enumdirection, float f, float f1, float f2, int i, EntityLiving entityliving) {

View File

@ -1,5 +1,6 @@
package net.minecraft.server; package net.minecraft.server;
import com.elevatemc.spigot.util.ObjectMapList;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Queues; import com.google.common.collect.Queues;
@ -163,7 +164,7 @@ public class Chunk {
this.heightMap = new int[256]; this.heightMap = new int[256];
for (int k = 0; k < this.entitySlices.length; ++k) { for (int k = 0; k < this.entitySlices.length; ++k) {
this.entitySlices[k] = new org.bukkit.craftbukkit.util.UnsafeList(); // Spigot this.entitySlices[k] = new ObjectMapList<>(); // IonSpigot - UnsafeList -> ObjectMapList // Spigot
} }
Arrays.fill(this.f, -999); Arrays.fill(this.f, -999);
@ -1034,6 +1035,58 @@ public class Chunk {
this.q = true; this.q = true;
} }
// IonSpigot start - Optimise Entity Collisions
public boolean collectEntitiesByAmount(Entity source, AxisAlignedBB axisalignedbb, List<Entity> entities,
Predicate<? super Entity> by, int amount) {
int i = MathHelper.floor((axisalignedbb.b - 2.0D) / 16.0D);
int j = MathHelper.floor((axisalignedbb.e + 2.0D) / 16.0D);
i = MathHelper.clamp(i, 0, this.entitySlices.length - 1);
j = MathHelper.clamp(j, 0, this.entitySlices.length - 1);
for (int k = i; k <= j; ++k) {
if (!this.entitySlices[k].isEmpty()) {
if (collectEntities(source, axisalignedbb, this.entitySlices[k], entities, by, amount)) {
return true;
}
}
}
return false;
}
private boolean collectEntities(Entity source, AxisAlignedBB axisalignedbb, List<Entity> slice,
List<Entity> entities, Predicate<? super Entity> by, int amount) {
for (int i = 0; i < slice.size(); ++i) {
int next = world.random.nextInt(slice.size());
Entity entity = slice.get(next);
if (entity.getBoundingBox().b(axisalignedbb) && entity != source && !entities.contains(entity)) {
if (by == null || by.apply(entity)) {
entities.add(entity);
}
Entity[] passengers = entity.aB();
if (passengers != null) {
for (Entity value : passengers) {
if (value != source && value.getBoundingBox().b(axisalignedbb) && (by == null
|| by.apply(value)) && !entities.contains(entity)) {
entities.add(value);
}
}
}
if (entities.size() >= amount) {
return true;
}
}
}
return false;
}
// IonSpigot end
public void a(Entity entity, AxisAlignedBB axisalignedbb, List<Entity> list, Predicate<? super Entity> predicate) { public void a(Entity entity, AxisAlignedBB axisalignedbb, List<Entity> list, Predicate<? super Entity> predicate) {
int i = MathHelper.floor((axisalignedbb.b - 2.0D) / 16.0D); int i = MathHelper.floor((axisalignedbb.b - 2.0D) / 16.0D);
int j = MathHelper.floor((axisalignedbb.e + 2.0D) / 16.0D); int j = MathHelper.floor((axisalignedbb.e + 2.0D) / 16.0D);
@ -1087,7 +1140,7 @@ public class Chunk {
// PaperSpigot start // PaperSpigot start
int[] counts; int[] counts;
if (ItemStack.class.isAssignableFrom(oclass)) { if (EntityItem.class.isAssignableFrom(oclass)) {
counts = itemCounts; counts = itemCounts;
} else if (IInventory.class.isAssignableFrom(oclass)) { } else if (IInventory.class.isAssignableFrom(oclass)) {
counts = inventoryEntityCounts; counts = inventoryEntityCounts;

View File

@ -23,7 +23,7 @@ import org.bukkit.event.world.ChunkUnloadEvent;
public class ChunkProviderServer implements IChunkProvider { public class ChunkProviderServer implements IChunkProvider {
private static final Logger b = LogManager.getLogger(); private static final Logger b = LogManager.getLogger();
public LongSet unloadQueue = new LongOpenHashSet(20); // CraftBukkit - LongHashSet // TacoSpigot - LongHashSet -> HashArraySet public LongSet unloadQueue = new LongOpenHashSet(); // CraftBukkit - LongHashSet // TacoSpigot - LongHashSet -> HashArraySet // IonSpigot - LongOpenHashSet
public Chunk emptyChunk; public Chunk emptyChunk;
public IChunkProvider chunkProvider; public IChunkProvider chunkProvider;
public IChunkLoader chunkLoader; // KigPaper - private -> public public IChunkLoader chunkLoader; // KigPaper - private -> public
@ -69,8 +69,9 @@ public class ChunkProviderServer implements IChunkProvider {
} }
public void queueUnload(int i, int j) { public void queueUnload(int i, int j) {
long key = LongHash.toLong(i, j); // IonSpigot - Only create key once
// PaperSpigot start - Asynchronous lighting updates // PaperSpigot start - Asynchronous lighting updates
Chunk chunk = chunks.get(LongHash.toLong(i, j)); Chunk chunk = chunks.get(key); // IonSpigot
if (chunk != null && chunk.world.paperSpigotConfig.useAsyncLighting && (chunk.pendingLightUpdates.get() > 0 || chunk.world.getTime() - chunk.lightUpdateTime < 20)) { if (chunk != null && chunk.world.paperSpigotConfig.useAsyncLighting && (chunk.pendingLightUpdates.get() > 0 || chunk.world.getTime() - chunk.lightUpdateTime < 20)) {
return; return;
} }
@ -89,9 +90,9 @@ public class ChunkProviderServer implements IChunkProvider {
if (this.world.worldProvider.e()) { if (this.world.worldProvider.e()) {
if (!this.world.c(i, j)) { if (!this.world.c(i, j)) {
// CraftBukkit start // CraftBukkit start
this.unloadQueue.add(LongHash.toLong(i, j)); // TacoSpigot - directly invoke LongHash this.unloadQueue.add(key); // TacoSpigot - directly invoke LongHash
Chunk c = chunks.get(LongHash.toLong(i, j)); Chunk c = chunks.get(key);
if (c != null) { if (c != null) {
c.mustSave = true; c.mustSave = true;
} }
@ -99,9 +100,9 @@ public class ChunkProviderServer implements IChunkProvider {
} }
} else { } else {
// CraftBukkit start // CraftBukkit start
this.unloadQueue.add(LongHash.toLong(i, j)); // TacoSpigot - directly invoke LongHash this.unloadQueue.add(key); // TacoSpigot - directly invoke LongHash
Chunk c = chunks.get(LongHash.toLong(i, j)); Chunk c = chunks.get(key);
if (c != null) { if (c != null) {
c.mustSave = true; c.mustSave = true;
} }
@ -128,7 +129,8 @@ public class ChunkProviderServer implements IChunkProvider {
} }
public Chunk getChunkAt(int i, int j, Runnable runnable) { public Chunk getChunkAt(int i, int j, Runnable runnable) {
Chunk chunk = chunks.get(LongHash.toLong(i, j)); long key = LongHash.toLong(i, j); // IonSpigot - Only create key once
Chunk chunk = chunks.get(key); // IonSpigot
ChunkRegionLoader loader = null; ChunkRegionLoader loader = null;
if (this.chunkLoader instanceof ChunkRegionLoader) { if (this.chunkLoader instanceof ChunkRegionLoader) {
@ -147,7 +149,7 @@ public class ChunkProviderServer implements IChunkProvider {
chunk = originalGetChunkAt(i, j); chunk = originalGetChunkAt(i, j);
} }
unloadQueue.remove(LongHash.toLong(i, j)); // SportPaper unloadQueue.remove(key); // SportPaper
// If we didn't load the chunk async and have a callback run it now // If we didn't load the chunk async and have a callback run it now
if (runnable != null) { if (runnable != null) {
runnable.run(); runnable.run();
@ -156,7 +158,8 @@ public class ChunkProviderServer implements IChunkProvider {
return chunk; return chunk;
} }
public Chunk originalGetChunkAt(int i, int j) { public Chunk originalGetChunkAt(int i, int j) {
Chunk chunk = this.chunks.get(LongHash.toLong(i, j)); long key = LongHash.toLong(i, j); // IonSpigot - Only create key once
Chunk chunk = this.chunks.get(key);
boolean newChunk = false; boolean newChunk = false;
// CraftBukkit end // CraftBukkit end
@ -173,8 +176,8 @@ public class ChunkProviderServer implements IChunkProvider {
CrashReport crashreport = CrashReport.a(throwable, "Exception generating new chunk"); CrashReport crashreport = CrashReport.a(throwable, "Exception generating new chunk");
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Chunk to be generated"); CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Chunk to be generated");
crashreportsystemdetails.a("Location", String.format("%d,%d", new Object[] {i, j})); crashreportsystemdetails.a("Location", String.format("%d,%d", i, j));
crashreportsystemdetails.a("Position hash", LongHash.toLong(i, j)); // CraftBukkit - Use LongHash crashreportsystemdetails.a("Position hash", key); // CraftBukkit - Use LongHash
crashreportsystemdetails.a("Generator", this.chunkProvider.getName()); crashreportsystemdetails.a("Generator", this.chunkProvider.getName());
throw new ReportedException(crashreport); throw new ReportedException(crashreport);
} }
@ -182,7 +185,7 @@ public class ChunkProviderServer implements IChunkProvider {
newChunk = true; // CraftBukkit newChunk = true; // CraftBukkit
} }
this.chunks.put(LongHash.toLong(i, j), chunk); this.chunks.put(key, chunk);
chunk.addEntities(); chunk.addEntities();
@ -216,16 +219,31 @@ public class ChunkProviderServer implements IChunkProvider {
world.timings.syncChunkLoadTimer.stopTiming(); // Spigot world.timings.syncChunkLoadTimer.stopTiming(); // Spigot
} }
this.unloadQueue.remove(LongHash.toLong(i, j)); // SportPaper this.unloadQueue.remove(key); // SportPaper
return chunk; return chunk;
} }
// IonSpigot start - Optimise Chunk Getting
private Chunk cachedChunk = null;
public Chunk getOrCreateChunk(int i, int j) { public Chunk getOrCreateChunk(int i, int j) {
Chunk chunk = cachedChunk; // We have to do this for thread safety
if (chunk != null && chunk.locX == i && chunk.locZ == j && chunk.o()) {
return chunk;
}
// CraftBukkit start // CraftBukkit start
Chunk chunk = this.chunks.get(LongHash.toLong(i, j)); chunk = this.chunks.get(LongHash.toLong(i, j));
chunk = chunk == null ? (!this.world.ad() && !this.forceChunkLoad ? this.emptyChunk : this.getChunkAt(i, j)) : chunk; if (chunk == null) {
if (!this.world.ad() && !this.forceChunkLoad) {
return this.emptyChunk;
}
chunk = this.getChunkAt(i, j);
}
cachedChunk = chunk;
/*
if (chunk == emptyChunk) return chunk; if (chunk == emptyChunk) return chunk;
if (i != chunk.locX || j != chunk.locZ) { if (i != chunk.locX || j != chunk.locZ) {
b.error("Chunk (" + chunk.locX + ", " + chunk.locZ + ") stored at (" + i + ", " + j + ") in world '" + world.getWorld().getName() + "'"); b.error("Chunk (" + chunk.locX + ", " + chunk.locZ + ") stored at (" + i + ", " + j + ") in world '" + world.getWorld().getName() + "'");
@ -233,7 +251,8 @@ public class ChunkProviderServer implements IChunkProvider {
Throwable ex = new Throwable(); Throwable ex = new Throwable();
ex.fillInStackTrace(); ex.fillInStackTrace();
ex.printStackTrace(); ex.printStackTrace();
} } */
// IonSpigot end
return chunk; return chunk;
// CraftBukkit end // CraftBukkit end

View File

@ -73,7 +73,10 @@ public class ChunkSection {
} }
public boolean a() { public boolean a() {
return this.nonEmptyBlockCount == 0; //return this.nonEmptyBlockCount == 0;
// Paper - MC-80966
// Even if there are no blocks, there may be other information associated with the chunk, always send it.
return false;
} }
public boolean shouldTick() { public boolean shouldTick() {

View File

@ -196,7 +196,7 @@ public class DedicatedServer extends MinecraftServer implements IMinecraftServer
if (!org.spigotmc.SpigotConfig.lateBind) { if (!org.spigotmc.SpigotConfig.lateBind) {
try { try {
this.aq().bind(bindAddress); // PandaSpigot - Unix domain socket support this.aq().a(bindAddress, this.R()); // PandaSpigot - Unix domain socket support
} catch (IOException ioexception) { } catch (IOException ioexception) {
DedicatedServer.LOGGER.warn("**** FAILED TO BIND TO PORT!"); DedicatedServer.LOGGER.warn("**** FAILED TO BIND TO PORT!");
DedicatedServer.LOGGER.warn("The exception was: {}", new Object[] { ioexception.toString()}); DedicatedServer.LOGGER.warn("The exception was: {}", new Object[] { ioexception.toString()});
@ -292,7 +292,7 @@ public class DedicatedServer extends MinecraftServer implements IMinecraftServer
if (org.spigotmc.SpigotConfig.lateBind) { if (org.spigotmc.SpigotConfig.lateBind) {
try { try {
this.aq().bind(bindAddress); // PandaSpigot - Unix domain socket support this.aq().a(bindAddress, this.R()); // PandaSpigot - Unix domain socket support
} catch (IOException ioexception) { } catch (IOException ioexception) {
DedicatedServer.LOGGER.warn("**** FAILED TO BIND TO PORT!"); DedicatedServer.LOGGER.warn("**** FAILED TO BIND TO PORT!");
DedicatedServer.LOGGER.warn("The exception was: {}", new Object[] { ioexception.toString()}); DedicatedServer.LOGGER.warn("The exception was: {}", new Object[] { ioexception.toString()});
@ -434,7 +434,15 @@ public class DedicatedServer extends MinecraftServer implements IMinecraftServer
} }
public boolean ai() { public boolean ai() {
return this.propertyManager.getBoolean("use-native-transport", true); return org.apache.commons.lang.SystemUtils.IS_OS_LINUX && this.getTransport() == ServerConnection.EventGroupType.EPOLL; // Nacho - Add a check to see if we are using Linux or not, if not ignore this.
}
public ServerConnection.EventGroupType getTransport() {
try {
return ServerConnection.EventGroupType.valueOf(this.propertyManager.getString("transport-to-use", "default").toUpperCase());
} catch (Exception ignored) {
return ServerConnection.EventGroupType.DEFAULT;
}
} }
public DedicatedPlayerList aP() { public DedicatedPlayerList aP() {

View File

@ -134,8 +134,13 @@ public abstract class Entity implements ICommandListener {
public boolean valid; // CraftBukkit public boolean valid; // CraftBukkit
public org.bukkit.projectiles.ProjectileSource projectileSource; // CraftBukkit - For projectiles only public org.bukkit.projectiles.ProjectileSource projectileSource; // CraftBukkit - For projectiles only
public boolean forceExplosionKnockback; // CraftBukkit - SPIGOT-949 public boolean forceExplosionKnockback; // CraftBukkit - SPIGOT-949
public boolean isCannoningEntity; // IonSpigot
public boolean inUnloadedChunk = false; // PaperSpigot - Remove entities in unloaded chunks public boolean inUnloadedChunk = false; // PaperSpigot - Remove entities in unloaded chunks
public boolean loadChunks = false; // PaperSpigot - Entities can load chunks they move through and keep them loaded public boolean loadChunks = false; // PaperSpigot - Entities can load chunks they move through and keep them loaded
// IonSpigot start - Lag Compensated Ticking
protected boolean compensated;
protected void tick() {}
// IonSpigot end
public static Random SHARED_RANDOM = new FastRandom() { public static Random SHARED_RANDOM = new FastRandom() {
private boolean locked = false; private boolean locked = false;
@ -206,6 +211,7 @@ public abstract class Entity implements ICommandListener {
this.defaultActivationState = false; this.defaultActivationState = false;
} }
// Spigot end // Spigot end
this.isCannoningEntity = this instanceof EntityTNTPrimed || this instanceof EntityFallingBlock; // IonSpigot
this.datawatcher = new DataWatcher(this); this.datawatcher = new DataWatcher(this);
this.datawatcher.a(0, (byte) 0); this.datawatcher.a(0, (byte) 0);
@ -455,15 +461,36 @@ public abstract class Entity implements ICommandListener {
* PaperSpigot - Load surrounding chunks the entity is moving through * PaperSpigot - Load surrounding chunks the entity is moving through
*/ */
public void loadChunks() { public void loadChunks() {
// IonSpigot start - Fix Load Chunks
/*
This implementation is flawed, as it does not work properly in south and east directions.
The reason for this is because the motion would be negative in those directions which
would cause the checks to fail, as it is missing min and max checks.
Now you're going to be saying my cannon loaded chunks in those directions,
you are right about that, the reason it works is because theres an additional layer
of chunk loading, I personally believe this method is meant to ensure that the current position
is loaded as it isn't guaranteed that the entity will move at all.
This additional layer is located in the getCubes method, we can remove the excess logic from here
and take advantage of that with the triangle patch that was implemented in TacoSpigot
this allows for triangle chunk loading allowing to us to reduce chunks loaded by cannons.
for (int cx = (int) locX >> 4; cx <= (int) (locX + motX) >> 4; ++cx) { for (int cx = (int) locX >> 4; cx <= (int) (locX + motX) >> 4; ++cx) {
for (int cz = (int) locZ >> 4; cz <= (int) (locZ + motZ) >> 4; ++cz) { for (int cz = (int) locZ >> 4; cz <= (int) (locZ + motZ) >> 4; ++cz) {
((ChunkProviderServer) world.chunkProvider).getChunkAt(cx, cz); ((ChunkProviderServer) world.chunkProvider).getChunkAt(cx, cz);
} }
} }
*/
int chunkX = org.bukkit.util.NumberConversions.floor(locX) >> 4;
int chunkZ = org.bukkit.util.NumberConversions.floor(locZ) >> 4;
((ChunkProviderServer) world.chunkProvider).getChunkAt(chunkX, chunkZ);
// IonSpigot end
} }
public void move(double d0, double d1, double d2) { public void move(double d0, double d1, double d2) {
// Check if we're moving
if (d0 == 0 && d1 == 0 && d2 == 0 && this.vehicle == null && this.passenger == null) {
return;
}
if (this.loadChunks) loadChunks(); // PaperSpigot - Load chunks if (this.loadChunks) loadChunks(); // PaperSpigot - Load chunks
// FlamePaper start - Disable Unloaded Chunk Movement // FlamePaper start - Disable Unloaded Chunk Movement
if (!world.chunkProvider.isChunkLoaded((int) locX >> 4, (int) locZ >> 4)) { if (!world.chunkProvider.isChunkLoaded((int) locX >> 4, (int) locZ >> 4)) {
@ -486,10 +513,6 @@ public abstract class Entity implements ICommandListener {
this.appendEntityCrashDetails(crashreportsystemdetails); this.appendEntityCrashDetails(crashreportsystemdetails);
throw new ReportedException(crashreport); throw new ReportedException(crashreport);
} }
// Check if we're moving
if (d0 == 0 && d1 == 0 && d2 == 0 && this.vehicle == null && this.passenger == null) {
return;
}
// CraftBukkit end // CraftBukkit end
this.world.methodProfiler.a("move"); this.world.methodProfiler.a("move");
double d3 = this.locX; double d3 = this.locX;
@ -1318,6 +1341,7 @@ public abstract class Entity implements ICommandListener {
public void b(Entity entity, int i) {} public void b(Entity entity, int i) {}
public boolean c(NBTTagCompound nbttagcompound) { public boolean c(NBTTagCompound nbttagcompound) {
if (this instanceof EntityFireworks || this instanceof EntityArrow)return false; // YAPFA - Don't save arrows or fireworks
String s = this.ag(); String s = this.ag();
if (!this.dead && s != null) { if (!this.dead && s != null) {

View File

@ -163,7 +163,7 @@ public class EntityBat extends EntityAmbient {
if (blockposition.getY() >= this.world.F()) { if (blockposition.getY() >= this.world.F()) {
return false; return false;
} else { } else {
int i = this.world.getLightLevel(blockposition); //int i = this.world.getLightLevel(blockposition); // Yatopia - moved down
byte b0 = 4; byte b0 = 4;
if (this.a(this.world.Y())) { if (this.a(this.world.Y())) {
@ -172,6 +172,7 @@ public class EntityBat extends EntityAmbient {
return false; return false;
} }
int i = this.world.getLightLevel(blockposition); // Yatopia - moved from above
return i > this.random.nextInt(b0) ? false : super.bR(); return i > this.random.nextInt(b0) ? false : super.bR();
} }
} }

View File

@ -1,6 +1,7 @@
package net.minecraft.server; package net.minecraft.server;
// CraftBukkit start // CraftBukkit start
import com.elevatemc.spigot.config.eSpigotConfig;
import com.elevatemc.spigot.event.PlayerPearlRefundEvent; import com.elevatemc.spigot.event.PlayerPearlRefundEvent;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -41,6 +42,12 @@ public class EntityEnderPearl extends EntityProjectile {
super(world, entityliving); super(world, entityliving);
this.c = entityliving; this.c = entityliving;
this.loadChunks = world.paperSpigotConfig.loadUnloadedEnderPearls; // PaperSpigot this.loadChunks = world.paperSpigotConfig.loadUnloadedEnderPearls; // PaperSpigot
// IonSpigot start - Lag Compensated Pearls
if (entityliving instanceof EntityPlayer && eSpigotConfig.lagCompensatedPearls) {
((EntityPlayer) entityliving).lagCompensatedTicking.add(this);
compensated = true;
}
// IonSpigot end
} }
@Override @Override
@ -152,8 +159,15 @@ public class EntityEnderPearl extends EntityProjectile {
} }
} }
// IonSpigot start - Lag Compensated Pearls
@Override @Override
public void t_() { public void t_() {
if (!compensated) {
tick();
}
}
public void tick() {
// IonSpigot end
final EntityLiving shooter = this.getShooter(); final EntityLiving shooter = this.getShooter();
// ImHacking Start - Prevent Pearling into blocks // ImHacking Start - Prevent Pearling into blocks

View File

@ -1376,7 +1376,7 @@ public abstract class EntityHuman extends EntityLiving {
} }
public void b(Statistic statistic) { public void b(Statistic statistic) {
this.a(statistic, 1); this.a(statistic, 20);
} }
public void a(Statistic statistic, int i) { public void a(Statistic statistic, int i) {

View File

@ -108,6 +108,7 @@ public abstract class EntityLiving extends Entity {
++this.ticksFarFromPlayer; // Above all the floats ++this.ticksFarFromPlayer; // Above all the floats
} }
// Spigot end // Spigot end
private int tick;
public void G() { public void G() {
this.damageEntity(DamageSource.OUT_OF_WORLD, Float.MAX_VALUE); this.damageEntity(DamageSource.OUT_OF_WORLD, Float.MAX_VALUE);
@ -1508,11 +1509,12 @@ public abstract class EntityLiving extends Entity {
} }
} }
tick++;
for (int j = 0; j < 5; ++j) { for (int j = 0; j < 5; ++j) {
ItemStack itemstack = this.h[j]; ItemStack itemstack = this.h[j];
ItemStack itemstack1 = this.getEquipment(j); ItemStack itemstack1 = this.getEquipment(j);
if (!ItemStack.matches(itemstack1, itemstack)) { if (!ItemStack.fastMatches(itemstack1, itemstack) || (this.tick % 20 == 0 && !ItemStack.matches(itemstack1, itemstack))) {
((WorldServer) this.world).getTracker().a(this, new PacketPlayOutEntityEquipment(this.getId(), j, itemstack1)); ((WorldServer) this.world).getTracker().a(this, new PacketPlayOutEntityEquipment(this.getId(), j, itemstack1));
if (itemstack != null) { if (itemstack != null) {
this.c.a(itemstack.B()); this.c.a(itemstack.B());
@ -1709,15 +1711,10 @@ public abstract class EntityLiving extends Entity {
if (!eSpigotConfig.entityCollisions) if (!eSpigotConfig.entityCollisions)
return; return;
List list = this.world.a(this, this.getBoundingBox().grow(0.20000000298023224D, 0.0D, 0.20000000298023224D), Predicates.and(IEntitySelector.d, new Predicate() { // IonSpigot start - Optimise Entity Collisions
public boolean a(Entity entity) { List list = this.world.getEntitiesByAmount(this, this.getBoundingBox().grow(0.20000000298023224D, 0.0D, 0.20000000298023224D),
return entity.ae(); input -> IEntitySelector.d.apply(input) && input != null && input.ae(), world.spigotConfig.maxCollisionsPerEntity);
} // IonSpigot end
public boolean apply(Object object) {
return this.a((Entity) object);
}
}));
if (this.ad() && !list.isEmpty()) { // Spigot: Add this.ad() condition if (this.ad() && !list.isEmpty()) { // Spigot: Add this.ad() condition
numCollisions -= world.spigotConfig.maxCollisionsPerEntity; // Spigot numCollisions -= world.spigotConfig.maxCollisionsPerEntity; // Spigot

View File

@ -30,9 +30,9 @@ public abstract class EntityMinecartAbstract extends Entity implements INamableT
private double derailedX = 0.5; private double derailedX = 0.5;
private double derailedY = 0.5; private double derailedY = 0.5;
private double derailedZ = 0.5; private double derailedZ = 0.5;
private double flyingX = 0.95; private double flyingX = 0.949999988079071D; // Paper - restore vanilla precision
private double flyingY = 0.95; private double flyingY = 0.949999988079071D; // Paper - restore vanilla precision
private double flyingZ = 0.95; private double flyingZ = 0.949999988079071D; // Paper - restore vanilla precision
public double maxSpeed = 0.4D; public double maxSpeed = 0.4D;
// CraftBukkit end // CraftBukkit end

View File

@ -51,6 +51,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
public boolean g; public boolean g;
public int ping; public int ping;
public boolean viewingCredits; public boolean viewingCredits;
public List<Entity> lagCompensatedTicking = new ArrayList<>(); // IonSpigot - Lag Compensated Ticking
// CraftBukkit start // CraftBukkit start
public String displayName; public String displayName;
@ -66,7 +67,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
// Spigot start // Spigot start
public boolean collidesWithEntities = true; public boolean collidesWithEntities = true;
public int viewDistance; // PaperSpigot - Player view distance API public int viewDistance; // PaperSpigot - Player view distance API
private int containerUpdateDelay; // PaperSpigot /* private int containerUpdateDelay; */ // PaperSpigot
public boolean positiveXMovement, positiveYMovement, positiveZMovement; public boolean positiveXMovement, positiveYMovement, positiveZMovement;
public KnockbackProfile knockbackProfile = eSpigot.getInstance().getKnockbackHandler().getActiveProfile(); public KnockbackProfile knockbackProfile = eSpigot.getInstance().getKnockbackHandler().getActiveProfile();
@ -209,11 +210,11 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
if (this.noDamageTicks > 0) { if (this.noDamageTicks > 0) {
--this.noDamageTicks; --this.noDamageTicks;
} }
// PaperSpigot start - Configurable container update tick rate // PaperSpigot start - Configurable container update tick rate
if (--containerUpdateDelay <= 0) { if (/*--containerUpdateDelay <= 0*/ true) {
this.activeContainer.b(); this.activeContainer.b();
containerUpdateDelay = world.paperSpigotConfig.containerUpdateTickRate; /*containerUpdateDelay = world.paperSpigotConfig.containerUpdateTickRate;*/
} }
// PaperSpigot end // PaperSpigot end
if (!this.world.isClientSide && !this.activeContainer.a(this)) { if (!this.world.isClientSide && !this.activeContainer.a(this)) {
@ -325,6 +326,21 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
public void l() { public void l() {
try { try {
super.t_(); super.t_();
// IonSpigot start - Lag Compensated Ticking
for (int i = 0; i < this.lagCompensatedTicking.size(); ++i) {
Entity entity = this.lagCompensatedTicking.get(i);
entity.tick();
// Check if size is > 9, this should cover some abuse
if (entity.dead || this.lagCompensatedTicking.size() > 9) {
if (!entity.dead) {
entity.compensated = false;
}
this.lagCompensatedTicking.remove(i--);
}
}
// IonSpigot end
for (int i = 0; i < this.inventory.getSize(); ++i) { for (int i = 0; i < this.inventory.getSize(); ++i) {
ItemStack itemstack = this.inventory.getItem(i); ItemStack itemstack = this.inventory.getItem(i);

View File

@ -6,12 +6,14 @@ import java.util.List;
// CraftBukkit start // CraftBukkit start
import java.util.HashMap; import java.util.HashMap;
import com.elevatemc.spigot.config.eSpigotConfig;
import org.bukkit.craftbukkit.entity.CraftLivingEntity; import org.bukkit.craftbukkit.entity.CraftLivingEntity;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
// CraftBukkit end // CraftBukkit end
public class EntityPotion extends EntityProjectile { public class EntityPotion extends EntityProjectile {
public boolean compensated = false; // IonSpigot - Lag Compensated Potions
public ItemStack item; public ItemStack item;
public EntityPotion(World world) { public EntityPotion(World world) {
@ -25,6 +27,12 @@ public class EntityPotion extends EntityProjectile {
public EntityPotion(World world, EntityLiving entityliving, ItemStack itemstack) { public EntityPotion(World world, EntityLiving entityliving, ItemStack itemstack) {
super(world, entityliving); super(world, entityliving);
this.item = itemstack; this.item = itemstack;
// IonSpigot start - Lag Compensated Potions
if (entityliving instanceof EntityPlayer && eSpigotConfig.lagCompensatedPotions) {
((EntityPlayer) entityliving).lagCompensatedTicking.add(this);
compensated = true;
}
// IonSpigot end
} }
public EntityPotion(World world, double d0, double d1, double d2, ItemStack itemstack) { public EntityPotion(World world, double d0, double d1, double d2, ItemStack itemstack) {
@ -66,6 +74,18 @@ public class EntityPotion extends EntityProjectile {
return this.item.getData(); return this.item.getData();
} }
// IonSpigot start - Lag Compensated Potions
@Override
public void t_() {
if (!compensated) {
tick();
}
}
public void tick() {
super.t_();
}
// IonSpigot end
protected void a(MovingObjectPosition movingobjectposition) { protected void a(MovingObjectPosition movingobjectposition) {
if (!this.world.isClientSide) { if (!this.world.isClientSide) {
List list = Items.POTION.h(this.item); List list = Items.POTION.h(this.item);

View File

@ -52,7 +52,7 @@ public class EntityTNTPrimed extends Entity {
} }
public void t_() { public void t_() {
if (world.spigotConfig.currentPrimedTnt++ > world.spigotConfig.maxTntTicksPerTick) { return; } // Spigot if (world.spigotConfig.maxTntTicksPerTick > -1 && world.spigotConfig.currentPrimedTnt++ > world.spigotConfig.maxTntTicksPerTick) { return; } // Spigo
this.lastX = this.locX; this.lastX = this.locX;
this.lastY = this.locY; this.lastY = this.locY;
this.lastZ = this.locZ; this.lastZ = this.locZ;
@ -181,6 +181,8 @@ public class EntityTNTPrimed extends Entity {
public boolean W() { public boolean W() {
if (!world.paperSpigotConfig.fixCannons) return super.W(); if (!world.paperSpigotConfig.fixCannons) return super.W();
// IonSpigot start - Optimise TNT Ticking
/*
// Preserve velocity while calling the super method // Preserve velocity while calling the super method
double oldMotX = this.motX; double oldMotX = this.motX;
double oldMotY = this.motY; double oldMotY = this.motY;
@ -208,6 +210,7 @@ public class EntityTNTPrimed extends Entity {
} }
} }
} }
*/
return this.inWater; return this.inWater;
} }

View File

@ -1,5 +1,7 @@
package net.minecraft.server; package net.minecraft.server;
import com.elevatemc.spigot.config.eSpigotConfig;
import com.elevatemc.spigot.visuals.CannonTrackerEntry;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
@ -107,7 +109,7 @@ public class EntityTracker {
final int finalI = i; // CraftBukkit - fix decompile error final int finalI = i; // CraftBukkit - fix decompile error
EntityTrackerEntry entitytrackerentry = new EntityTrackerEntry(entity, finalI, j, flag); EntityTrackerEntry entitytrackerentry = createTracker(entity, i, j, flag); // IonSpigot
// We want to add them to these collections directly to ensure compatibility with plugins like Citizens // We want to add them to these collections directly to ensure compatibility with plugins like Citizens
@ -153,6 +155,16 @@ public class EntityTracker {
}); });
} }
// IonSpigot start
private EntityTrackerEntry createTracker(Entity entity, int i, int j, boolean flag) {
if (entity.isCannoningEntity && eSpigotConfig.cannonTracker) {
return new CannonTrackerEntry(entity, i, j, flag);
} else {
return new EntityTrackerEntry(entity, i, j, flag);
}
}
// IonSpigot end
public void untrackEntity(Entity entity) { public void untrackEntity(Entity entity) {
//org.spigotmc.AsyncCatcher.catchOp( "entity untrack"); // Spigot //org.spigotmc.AsyncCatcher.catchOp( "entity untrack"); // Spigot
boolean mainThread = Thread.currentThread() == MinecraftServer.getServer().primaryThread; boolean mainThread = Thread.currentThread() == MinecraftServer.getServer().primaryThread;

View File

@ -461,7 +461,7 @@ public class EntityTrackerEntry {
return d0 >= (double) (-this.b) && d0 <= (double) this.b && d1 >= (double) (-this.b) && d1 <= (double) this.b && this.tracker.a(entityplayer); return d0 >= (double) (-this.b) && d0 <= (double) this.b && d1 >= (double) (-this.b) && d1 <= (double) this.b && this.tracker.a(entityplayer);
} }
private boolean e(EntityPlayer entityplayer) { protected boolean e(EntityPlayer entityplayer) { // IonSpigot - private -> protected
return entityplayer.u().getPlayerChunkMap().a(entityplayer, this.tracker.ae, this.tracker.ag); return entityplayer.u().getPlayerChunkMap().a(entityplayer, this.tracker.ae, this.tracker.ag);
} }
@ -471,7 +471,7 @@ public class EntityTrackerEntry {
} }
} }
private Packet c() { protected Packet c() {
if (this.tracker.dead) { if (this.tracker.dead) {
// CraftBukkit start - Remove useless error spam, just return // CraftBukkit start - Remove useless error spam, just return
// EntityTrackerEntry.p.warn("Fetching addPacket for removed entity"); // EntityTrackerEntry.p.warn("Fetching addPacket for removed entity");

View File

@ -174,6 +174,10 @@ public enum EnumProtocol {
return this.i; return this.i;
} }
public int getStateId() { // OBFHELPER
return a();
}
public static EnumProtocol a(int i) { public static EnumProtocol a(int i) {
return i >= EnumProtocol.e && i <= EnumProtocol.f ? EnumProtocol.g[i - EnumProtocol.e] : null; return i >= EnumProtocol.e && i <= EnumProtocol.f ? EnumProtocol.g[i - EnumProtocol.e] : null;
} }
@ -182,6 +186,10 @@ public enum EnumProtocol {
return (EnumProtocol) EnumProtocol.h.get(packet.getClass()); return (EnumProtocol) EnumProtocol.h.get(packet.getClass());
} }
public static EnumProtocol getProtocolForPacket(Packet packet) { // OBFHELPER
return a(packet);
}
EnumProtocol(int i, Object object) { EnumProtocol(int i, Object object) {
this(i); this(i);
} }

View File

@ -1,26 +1,27 @@
package net.minecraft.server; package net.minecraft.server;
import com.elevatemc.spigot.config.eSpigotConfig;
import com.elevatemc.spigot.threading.ThreadingManager;
import com.elevatemc.spigot.util.Constants; import com.elevatemc.spigot.util.Constants;
import com.elevatemc.spigot.util.FastRandom;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
// Nacho start
import net.jafama.FastMath;
// Nacho end
// CraftBukkit start // CraftBukkit start
import org.bukkit.craftbukkit.event.CraftEventFactory;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.craftbukkit.event.CraftEventFactory;
import org.bukkit.event.block.BlockExplodeEvent; import org.bukkit.event.block.BlockExplodeEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
// CraftBukkit end // CraftBukkit end
import java.util.*;
import java.util.concurrent.CompletableFuture;
public class Explosion { public class Explosion {
public static final Random CACHED_RANDOM = new FastRandom(); // AW-Spigot - fast random public static final Random CACHED_RANDOM = new Random();
private final boolean a; private final boolean a;
private final boolean b; private final boolean b;
private final Random c = CACHED_RANDOM; private final Random c = CACHED_RANDOM;
@ -51,118 +52,110 @@ public class Explosion {
return; return;
} }
// CraftBukkit end // CraftBukkit end
HashSet hashset = Sets.newHashSet(); // HashSet<BlockPosition> hashset = Sets.newHashSet();
boolean flag = true;
int i; int i;
int j; int j;
Block b = world.getChunkAt((int)posX >> 4, (int)posZ >> 4).getBlockData(new BlockPosition(posX, posY, posZ)).getBlock(); // TacoSpigot - get block of the explosion // IonSpigot start - Block Searching Improvements
BlockPosition pos = new BlockPosition(posX, posY, posZ);
Chunk chunk = world.getChunkAt(pos.getX() >> 4, pos.getZ() >> 4);
Block b = chunk.getBlockData(pos).getBlock(); // TacoSpigot - get block of the explosion
if (!this.world.paperSpigotConfig.optimizeLiquidExplosions || !b.getMaterial().isLiquid()) { //TacoSpigot - skip calculating what blocks to blow up in water/lava if (!this.world.paperSpigotConfig.optimizeLiquidExplosions || !b.getMaterial().isLiquid()) { // TacoSpigot - skip calculating what blocks to blow up in water/lava
for (int k = 0; k < 16; ++k) { it.unimi.dsi.fastutil.longs.LongSet set = new it.unimi.dsi.fastutil.longs.LongOpenHashSet();
for (i = 0; i < 16; ++i) { searchForBlocks(set, chunk);
for (j = 0; j < 16; ++j) { for (it.unimi.dsi.fastutil.longs.LongIterator iterator = set.iterator(); iterator.hasNext(); ) {
if (k == 0 || k == 15 || i == 0 || i == 15 || j == 0 || j == 15) { this.blocks.add(BlockPosition.fromLong(iterator.nextLong()));
double d0 = (float) k / 15.0F * 2.0F - 1.0F;
double d1 = (float) i / 15.0F * 2.0F - 1.0F;
double d2 = (float) j / 15.0F * 2.0F - 1.0F;
double d3 = Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2);
d0 /= d3;
d1 /= d3;
d2 /= d3;
float f = this.size * (0.7F + this.world.random.nextFloat() * 0.6F);
double d4 = this.posX;
double d5 = this.posY;
double d6 = this.posZ;
for (float f1 = 0.3F; f > 0.0F; f -= 0.22500001F) {
BlockPosition blockposition = new BlockPosition(d4, d5, d6);
IBlockData iblockdata = this.world.getType(blockposition);
if (iblockdata.getBlock().getMaterial() != Material.AIR) {
float f2 = this.source != null ? this.source.a(this, this.world, blockposition, iblockdata) : iblockdata.getBlock().a((Entity) null);
f -= (f2 + 0.3F) * 0.3F;
}
if (f > 0.0F && (this.source == null || this.source.a(this, this.world, blockposition, iblockdata, f)) && blockposition.getY() < 256 && blockposition.getY() >= 0) { // CraftBukkit - don't wrap explosions
hashset.add(blockposition);
}
d4 += d0 * 0.30000001192092896D;
d5 += d1 * 0.30000001192092896D;
d6 += d2 * 0.30000001192092896D;
}
}
}
} }
} }
}
this.blocks.addAll(hashset); // this.blocks.addAll(hashset);
float f3 = this.size * 2.0F; float f3 = this.size * 2.0F;
i = MathHelper.floor(this.posX - (double) f3 - 1.0D); // IonSpigot start - Faster Entity Iteration
j = MathHelper.floor(this.posX + (double) f3 + 1.0D); i = MathHelper.floor(this.posX - (double) f3 - 1.0D) >> 4;
int l = MathHelper.floor(this.posY - (double) f3 - 1.0D); j = MathHelper.floor(this.posX + (double) f3 + 1.0D) >> 4;
int i1 = MathHelper.floor(this.posY + (double) f3 + 1.0D); int l = MathHelper.clamp(MathHelper.floor(this.posY - (double) f3 - 1.0D) >> 4, 0, 15);
int j1 = MathHelper.floor(this.posZ - (double) f3 - 1.0D); int i1 = MathHelper.clamp(MathHelper.floor(this.posY + (double) f3 + 1.0D) >> 4, 0, 15);
int k1 = MathHelper.floor(this.posZ + (double) f3 + 1.0D); int j1 = MathHelper.floor(this.posZ - (double) f3 - 1.0D) >> 4;
int k1 = MathHelper.floor(this.posZ + (double) f3 + 1.0D) >> 4;
// PaperSpigot start - Fix lag from explosions processing dead entities // PaperSpigot start - Fix lag from explosions processing dead entities
List list = this.world.a(this.source, new AxisAlignedBB(i, l, j1, j, i1, k1), entity -> IEntitySelector.d.apply(entity) && !entity.dead); // List<Entity> list = this.world.a(this.source, new AxisAlignedBB(i, l, j1, j, i1, k1), entity -> IEntitySelector.d.apply(entity) && !entity.dead);
// PaperSpigot end // PaperSpigot end
Vec3D vec3d = new Vec3D(this.posX, this.posY, this.posZ); Vec3D vec3d = new Vec3D(this.posX, this.posY, this.posZ);
for (Object o : list) { for (int chunkX = i; chunkX <= j; ++chunkX) {
Entity entity = (Entity) o; for (int chunkZ = j1; chunkZ <= k1; ++chunkZ) {
chunk = world.getChunkIfLoaded(chunkX, chunkZ);
if (chunk == null) {
continue;
}
for (int chunkY = l; chunkY <= i1; ++chunkY) {
affectEntities(chunk.entitySlices[chunkY], vec3d, f3);
}
}
}
}
public void affectEntities(List<Entity> list, Vec3D vec3d, float f3) {
for (Entity entity : list) {
if (!entity.aW()) { if (!entity.aW()) {
double d7 = entity.f(this.posX, this.posY, this.posZ) / (double) f3; if (!entity.dead) {
if (d7 <= 1.0D) {
double d8 = entity.locX - this.posX; double d8 = entity.locX - this.posX;
double d9 = entity.locY + (double) entity.getHeadHeight() - this.posY; double d9 = entity.locY + entity.getHeadHeight() - this.posY;
double d10 = entity.locZ - this.posZ; double d10 = entity.locZ - this.posZ;
double d11 = MathHelper.sqrt(d8 * d8 + d9 * d9 + d10 * d10); double distanceSquared = d8 * d8 + d9 * d9 + d10 * d10;
if (d11 != 0.0D) { if (distanceSquared <= 64.0D && distanceSquared != 0.0D) {
double d11 = MathHelper.sqrt(distanceSquared);
double d7 = d11 / (double) f3;
d8 /= d11; d8 /= d11;
d9 /= d11; d9 /= d11;
d10 /= d11; d10 /= d11;
double d12 = this.getBlockDensity(vec3d, entity.getBoundingBox()); // PaperSpigot - Optimize explosions
double d13 = (1.0D - d7) * d12;
// entity.damageEntity(DamageSource.explosion(this), (float) ((int) ((d13 * d13 + d13) / 2.0D * 8.0D * (double) f3 + 1.0D)));+ // CraftBukkit start // Paper - Optimize explosions
CraftEventFactory.entityDamage = source; // double d12 = this.getBlockDensity(vec3d, entity);
entity.forceExplosionKnockback = false; double finalD = d8;
boolean wasDamaged = entity.damageEntity(DamageSource.explosion(this), (float) ((int) ((d13 * d13 + d13) / 2.0D * 8.0D * (double) f3 + 1.0D))); double finalD1 = d9;
CraftEventFactory.entityDamage = null; double finalD11 = d10;
if (!wasDamaged && !(entity instanceof EntityTNTPrimed || entity instanceof EntityFallingBlock) && !entity.forceExplosionKnockback) { this.getBlockDensity(vec3d, entity.getBoundingBox()).thenAccept((d12) -> MinecraftServer.getServer().addMainThreadTask(() -> {
continue; double d13 = (1.0D - d7) * d12;
}
// CraftBukkit end
double d14 = entity instanceof EntityHuman && world.paperSpigotConfig.disableExplosionKnockback ? 0 : EnchantmentProtection.a(entity, d13); // PaperSpigot
// PaperSpigot start - Fix cannons if (entity.isCannoningEntity) {
/* entity.g(finalD * d13, finalD1 * d13, finalD11 * d13);
entity.motX += d8 * d14; return;
entity.motY += d9 * d14; }
entity.motZ += d10 * d14; // IonSpigot end
*/
// This impulse method sets the dirty flag, so clients will get an immediate velocity update
entity.g(d8 * d14, d9 * d14, d10 * d14);
// PaperSpigot end
if (entity instanceof EntityHuman && !((EntityHuman) entity).abilities.isInvulnerable && !world.paperSpigotConfig.disableExplosionKnockback) { // PaperSpigot // entity.damageEntity(DamageSource.explosion(this), (float) ((int) ((d13 * d13 + d13) / 2.0D * 8.0D * (double) f3 + 1.0D))); // CraftBukkit start
this.k.put((EntityHuman) entity, new Vec3D(d8 * d13, d9 * d13, d10 * d13)); CraftEventFactory.entityDamage = source;
} entity.forceExplosionKnockback = false;
boolean wasDamaged = entity.damageEntity(DamageSource.explosion(this), (float) ((int) ((d13 * d13 + d13) / 2.0D * 8.0D * (double) f3 + 1.0D)));
CraftEventFactory.entityDamage = null;
if (!wasDamaged && !(entity instanceof EntityTNTPrimed || entity instanceof EntityFallingBlock) && !entity.forceExplosionKnockback) {
return;
}
// CraftBukkit end
double d14 = entity instanceof EntityHuman && world.paperSpigotConfig.disableExplosionKnockback ? 0 : EnchantmentProtection.a(entity, d13); // PaperSpigot
// PaperSpigot start - Fix cannons
// This impulse method sets the dirty flag, so clients will get an immediate velocity update
entity.g(finalD * d14, finalD1 * d14, finalD11 * d14);
// PaperSpigot end
if (entity instanceof EntityHuman && !((EntityHuman) entity).abilities.isInvulnerable && !world.paperSpigotConfig.disableExplosionKnockback) { // PaperSpigot
this.k.put((EntityHuman) entity, new Vec3D(finalD * d13, finalD1 * d13, finalD11 * d13));
}
}));
} }
} }
} }
} }
} }
public void a(boolean flag) { public void a(boolean flag) {
@ -194,18 +187,20 @@ public class Explosion {
} }
} }
boolean cancelled; boolean cancelled = false;
List<org.bukkit.block.Block> bukkitBlocks; List<org.bukkit.block.Block> bukkitBlocks = blockList;
float yield; float yield = 0.3F; // default
if (explode != null) { if (explode != null) {
EntityExplodeEvent event = new EntityExplodeEvent(explode, location, blockList, 0.3F); if (eSpigotConfig.fireEntityExplodeEvent) {
this.world.getServer().getPluginManager().callEvent(event); EntityExplodeEvent event = new EntityExplodeEvent(explode, location, blockList, yield);
cancelled = event.isCancelled(); this.world.getServer().getPluginManager().callEvent(event);
bukkitBlocks = event.blockList(); cancelled = event.isCancelled();
yield = event.getYield(); bukkitBlocks = event.blockList();
yield = event.getYield();
}
} else { } else {
BlockExplodeEvent event = new BlockExplodeEvent(location.getBlock(), blockList, 0.3F); BlockExplodeEvent event = new BlockExplodeEvent(location.getBlock(), blockList, yield);
this.world.getServer().getPluginManager().callEvent(event); this.world.getServer().getPluginManager().callEvent(event);
cancelled = event.isCancelled(); cancelled = event.isCancelled();
bukkitBlocks = event.blockList(); bukkitBlocks = event.blockList();
@ -231,6 +226,8 @@ public class Explosion {
Block block = this.world.getType(blockposition).getBlock(); Block block = this.world.getType(blockposition).getBlock();
world.spigotConfig.antiXrayInstance.updateNearbyBlocks(world, blockposition); // Spigot world.spigotConfig.antiXrayInstance.updateNearbyBlocks(world, blockposition); // Spigot
// IonSpigot start - Optimise Explosions
/*
if (flag) { if (flag) {
double d0 = (float) blockposition.getX() + this.world.random.nextFloat(); double d0 = (float) blockposition.getX() + this.world.random.nextFloat();
double d1 = (float) blockposition.getY() + this.world.random.nextFloat(); double d1 = (float) blockposition.getY() + this.world.random.nextFloat();
@ -249,9 +246,11 @@ public class Explosion {
d3 *= d7; d3 *= d7;
d4 *= d7; d4 *= d7;
d5 *= d7; d5 *= d7;
this.world.addParticle(EnumParticle.EXPLOSION_NORMAL, (d0 + this.posX * 1.0D) / 2.0D, (d1 + this.posY * 1.0D) / 2.0D, (d2 + this.posZ * 1.0D) / 2.0D, d3, d4, d5, Constants.EMPTY_ARRAY); this.world.addParticle(EnumParticle.EXPLOSION_NORMAL, (d0 + this.posX) / 2.0D, (d1 + this.posY) / 2.0D, (d2 + this.posZ) / 2.0D, d3, d4, d5);
this.world.addParticle(EnumParticle.SMOKE_NORMAL, d0, d1, d2, d3, d4, d5, Constants.EMPTY_ARRAY); this.world.addParticle(EnumParticle.SMOKE_NORMAL, d0, d1, d2, d3, d4, d5);
} }
*/
// IonSpigot end
if (block.getMaterial() != Material.AIR) { if (block.getMaterial() != Material.AIR) {
if (block.a(this)) { if (block.a(this)) {
@ -270,6 +269,7 @@ public class Explosion {
while (iterator.hasNext()) { while (iterator.hasNext()) {
blockposition = (BlockPosition) iterator.next(); blockposition = (BlockPosition) iterator.next();
// Nacho - revert >> // Nacho - optimize TNT by Lew_x
if (this.world.getType(blockposition).getBlock().getMaterial() == Material.AIR && this.world.getType(blockposition.down()).getBlock().o() && this.c.nextInt(3) == 0) { if (this.world.getType(blockposition).getBlock().getMaterial() == Material.AIR && this.world.getType(blockposition.down()).getBlock().o() && this.c.nextInt(3) == 0) {
// CraftBukkit start - Ignition by explosion // CraftBukkit start - Ignition by explosion
if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this).isCancelled()) { if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this).isCancelled()) {
@ -300,22 +300,184 @@ public class Explosion {
return this.blocks; return this.blocks;
} }
// PaperSpigot start - Optimize explosions // IonSpigot start - Block Searching Improvements
private float getBlockDensity(Vec3D vec3d, AxisAlignedBB aabb) { private final static List<double[]> VECTORS = Lists.newArrayListWithCapacity(1352);
if (!this.world.paperSpigotConfig.optimizeExplosions) {
return this.world.a(vec3d, aabb);
}
CacheKey key = new CacheKey(this, aabb); static {
Float blockDensity = this.world.explosionDensityCache.get(key); for (int k = 0; k < 16; ++k) {
if (blockDensity == null) { for (int i = 0; i < 16; ++i) {
blockDensity = this.world.a(vec3d, aabb); for (int j = 0; j < 16; ++j) {
this.world.explosionDensityCache.put(key, blockDensity); if (k == 0 || k == 15 || i == 0 || i == 15 || j == 0 || j == 15) {
} double d0 = (float) k / 15.0F * 2.0F - 1.0F;
double d1 = (float) i / 15.0F * 2.0F - 1.0F;
double d2 = (float) j / 15.0F * 2.0F - 1.0F;
double d3 = (eSpigotConfig.fastMath ? FastMath.sqrt(d0 * d0 + d1 * d1 + d2 * d2) : Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2));
return blockDensity; d0 = (d0 / d3) * 0.30000001192092896D;
d1 = (d1 / d3) * 0.30000001192092896D;
d2 = (d2 / d3) * 0.30000001192092896D;
VECTORS.add(new double[]{d0, d1, d2});
}
}
}
}
} }
// https://github.com/jellysquid3/lithium-fabric/blob/1.16.x/dev/src/main/java/me/jellysquid/mods/lithium/mixin/world/explosions/ExplosionMixin.java
private void searchForBlocks(it.unimi.dsi.fastutil.longs.LongSet set, Chunk chunk) {
BlockPosition.MutableBlockPosition position = new BlockPosition.MutableBlockPosition();
for (double[] vector : VECTORS) {
double d0 = vector[0];
double d1 = vector[1];
double d2 = vector[2];
float f = this.size * (0.7F + (world.paperSpigotConfig.constantExplosions ? 0.7F : this.world.random.nextFloat()) * 0.6F);
float resistance = 0;
double stepX = this.posX;
double stepY = this.posY;
double stepZ = this.posZ;
for (; f > 0.0F; f -= 0.22500001F) {
int floorX = (eSpigotConfig.fastMath ? FastMath.floorToInt((Double.doubleToRawLongBits(stepX) >>> 63)) : org.bukkit.util.NumberConversions.floor(stepX));
int floorY = (eSpigotConfig.fastMath ? FastMath.floorToInt((Double.doubleToRawLongBits(stepY) >>> 63)) : org.bukkit.util.NumberConversions.floor(stepY));
int floorZ = (eSpigotConfig.fastMath ? FastMath.floorToInt((Double.doubleToRawLongBits(stepZ) >>> 63)) : org.bukkit.util.NumberConversions.floor(stepZ));
if (position.getX() != floorX || position.getY() != floorY || position.getZ() != floorZ) {
position.setValues(floorX, floorY, floorZ);
int chunkX = floorX >> 4;
int chunkZ = floorZ >> 4;
if (chunk == null || !chunk.o() || chunk.locX != chunkX || chunk.locZ != chunkZ) {
chunk = world.getChunkAt(chunkX, chunkZ);
}
IBlockData iblockdata = chunk.getBlockData(position);
Block block = iblockdata.getBlock();
if (block != Blocks.AIR) {
float blockResistance = block.durability / 5.0f;
resistance = (blockResistance + 0.3F) * 0.3F;
f -= resistance;
if (f > 0.0F && (this.source == null || this.source.a(this, this.world, position, iblockdata, f)) && position.getY() < 256 && position.getY() >= 0) { // CraftBukkit - don't wrap explosions
set.add(position.asLong());
}
}
} else {
f -= resistance;
}
stepX += d0;
stepY += d1;
stepZ += d2;
}
}
}
// IonSpigot end
// Paper start - Optimize explosions
private CompletableFuture<Float> getBlockDensity(Vec3D vec3d, AxisAlignedBB aabb) {
return CompletableFuture.supplyAsync(() -> {
// IonSpigot start - Optimise Density Cache
int key = createKey(this, aabb);
float blockDensity = this.world.explosionDensityCache.get(key);
if (blockDensity == -1.0f) {
blockDensity = calculateDensity(vec3d, aabb);
this.world.explosionDensityCache.put(key, blockDensity);
}
return blockDensity;
}, ThreadingManager.asyncExplosionsExecutor);
}
private float calculateDensity(Vec3D vec3d, AxisAlignedBB aabb) {
if (world.paperSpigotConfig.reducedDensityRays) {
return calculateDensityReducedRays(vec3d, aabb);
} else {
return this.world.a(vec3d, aabb);
}
}
private float calculateDensityReducedRays(Vec3D vec3d, AxisAlignedBB aabb) {
int arrived = 0;
int rays = 0;
for (Vec3D vector : calculateVectors(aabb)) {
// If rays from the corners don't hit a block
// it should be safe to return the best outcome
if (rays == 8 && arrived == 8) {
return 1.0F;
}
if (world.rayTrace(vector, vec3d) == null) {
++arrived;
}
++rays;
}
return (float) arrived / (float) rays;
}
private List<Vec3D> calculateVectors(AxisAlignedBB aabb) {
double d0 = 1.0D / ((aabb.d - aabb.a) * 2.0D + 1.0D);
double d1 = 1.0D / ((aabb.e - aabb.b) * 2.0D + 1.0D);
double d2 = 1.0D / ((aabb.f - aabb.c) * 2.0D + 1.0D);
double d3 = (1.0D - ((eSpigotConfig.fastMath ? FastMath.floor(1.0D / d0) : Math.floor(1.0D / d0)) * d0)) / 2.0D;
double d4 = (1.0D - ((eSpigotConfig.fastMath ? FastMath.floor(1.0D / d2) : Math.floor(1.0D / d2)) * d2)) / 2.0D;
if (d0 < 0.0 || d1 < 0.0 || d2 < 0.0) {
return Collections.emptyList();
}
List<Vec3D> vectors = new LinkedList<>();
for (float f = 0.0F; f <= 1.0F; f = (float) ((double) f + d0)) {
for (float f1 = 0.0F; f1 <= 1.0F; f1 = (float) ((double) f1 + d1)) {
for (float f2 = 0.0F; f2 <= 1.0F; f2 = (float) ((double) f2 + d2)) {
double d5 = aabb.a + (aabb.d - aabb.a) * (double) f;
double d6 = aabb.b + (aabb.e - aabb.b) * (double) f1;
double d7 = aabb.c + (aabb.f - aabb.c) * (double) f2;
Vec3D vector = new Vec3D(d5 + d3, d6, d7 + d4);
if ((f == 0 || f + d0 > 1.0F) && (f1 == 0 || f1 + d1 > 1.0F) && (f2 == 0 || f2 + d2 > 1.0F)) {
vectors.add(0, vector);
} else {
vectors.add(vector);
}
}
}
}
return vectors;
}
static int createKey(Explosion explosion, AxisAlignedBB aabb) {
int result;
long temp;
result = explosion.world.hashCode();
temp = Double.doubleToLongBits(explosion.posX);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(explosion.posY);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(explosion.posZ);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(aabb.a);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(aabb.b);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(aabb.c);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(aabb.d);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(aabb.e);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(aabb.f);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
/* :: IonSpigot - comment this out
static class CacheKey { static class CacheKey {
private final World world; private final World world;
private final double posX, posY, posZ; private final double posX, posY, posZ;
@ -327,12 +489,12 @@ public class Explosion {
this.posX = explosion.posX; this.posX = explosion.posX;
this.posY = explosion.posY; this.posY = explosion.posY;
this.posZ = explosion.posZ; this.posZ = explosion.posZ;
this.minX = aabb.a; this.minX = aabb.getMinX();
this.minY = aabb.b; this.minY = aabb.getMinY();
this.minZ = aabb.c; this.minZ = aabb.getMinZ();
this.maxX = aabb.d; this.maxX = aabb.getMaxX();
this.maxY = aabb.e; this.maxY = aabb.getMaxY();
this.maxZ = aabb.f; this.maxZ = aabb.getMaxZ();
} }
@Override @Override
@ -380,5 +542,7 @@ public class Explosion {
return result; return result;
} }
} }
// PaperSpigot end */
} // IonSpigot end
// Paper end
}

View File

@ -139,7 +139,7 @@ public class LoginListener implements PacketLoginInListener, IUpdatePlayerListBo
if (this.server.aK() >= 0 && !this.networkManager.c()) { if (this.server.aK() >= 0 && !this.networkManager.c()) {
this.networkManager.a(new PacketLoginOutSetCompression(this.server.aK()), new ChannelFutureListener() { this.networkManager.a(new PacketLoginOutSetCompression(this.server.aK()), new ChannelFutureListener() {
public void a(ChannelFuture channelfuture) throws Exception { public void a(ChannelFuture channelfuture) throws Exception {
LoginListener.this.networkManager.a(LoginListener.this.server.aK()); LoginListener.this.networkManager.setupCompression(LoginListener.this.server.aK());
} }
public void operationComplete(ChannelFuture future) throws Exception { // CraftBukkit - fix decompile error public void operationComplete(ChannelFuture future) throws Exception { // CraftBukkit - fix decompile error

View File

@ -1,5 +1,8 @@
package net.minecraft.server; package net.minecraft.server;
import com.elevatemc.spigot.config.eSpigotConfig;
import net.jafama.FastMath;
import java.util.Random; import java.util.Random;
import java.util.UUID; import java.util.UUID;
@ -11,30 +14,34 @@ public class MathHelper {
private static final double d; private static final double d;
private static final double[] e; private static final double[] e;
private static final double[] f; private static final double[] f;
private static final boolean fastMathMode = eSpigotConfig.fastMath;
private static final boolean fastMathCosSin = eSpigotConfig.fastMathCosSin;
public static float sin(float f) { public static float sin(float f) {
return MathHelper.b[(int) (f * 10430.378F) & '\uffff']; return (fastMathCosSin ? ((float) FastMath.sinQuick(b[(int)(f * 10430.378F) & '\uffff'])) : (b[(int)(f * 10430.378F) & '\uffff']));
} }
public static float cos(float f) { public static float cos(float f) {
return MathHelper.b[(int) (f * 10430.378F + 16384.0F) & '\uffff']; return (fastMathCosSin ? ((float) FastMath.cosQuick(b[(int)(f * 10430.378F + 16384.0F) & '\uffff'])) : (b[(int)(f * 10430.378F + 16384.0F) & '\uffff']));
} }
public static float c(float f) { public static float c(float f) {
return (float) Math.sqrt((double) f); return (float) (fastMathMode ? (FastMath.sqrt(f)) : (Math.sqrt(f)));
} }
public static float sqrt(double d0) { public static float sqrt(double d0) {
return (float) Math.sqrt(d0); return (float) (fastMathMode ? (FastMath.sqrt(d0)) : (Math.sqrt(d0)));
} }
public static int d(float f) { public static int d(float f) {
if (fastMathMode) return FastMath.floorToInt(f);
int i = (int) f; int i = (int) f;
return f < (float) i ? i - 1 : i; return f < (float) i ? i - 1 : i;
} }
public static int floor(double d0) { public static int floor(double d0) {
if (fastMathMode) return FastMath.floorToInt(d0);
int i = (int) d0; int i = (int) d0;
return d0 < (double) i ? i - 1 : i; return d0 < (double) i ? i - 1 : i;
@ -55,12 +62,15 @@ public class MathHelper {
} }
public static int f(float f) { public static int f(float f) {
if (fastMathMode) return FastMath.ceilToInt(f);
int i = (int) f; int i = (int) f;
return f > (float) i ? i + 1 : i; return f > (float) i ? i + 1 : i;
} }
public static int f(double d0) { public static int f(double d0) {
if (fastMathMode) return FastMath.ceilToInt(d0);
int i = (int) d0; int i = (int) d0;
return d0 > (double) i ? i + 1 : i; return d0 > (double) i ? i + 1 : i;
@ -155,7 +165,7 @@ public class MathHelper {
} }
public static int a(String s, int i, int j) { public static int a(String s, int i, int j) {
return Math.max(j, a(s, i)); return (fastMathMode ? (FastMath.max(j, a(s, i))) : (Math.max(j, a(s, i))));
} }
public static double a(String s, double d0) { public static double a(String s, double d0) {
@ -261,11 +271,11 @@ public class MathHelper {
double d10 = d5 + d9; double d10 = d5 + d9;
if (flag2) { if (flag2) {
d10 = 1.5707963267948966D - d10; d10 = (fastMathMode ? (FastMath.PI / 2) : (1.5707963267948966D)) - d10;
} }
if (flag1) { if (flag1) {
d10 = 3.141592653589793D - d10; d10 = (fastMathMode ? (FastMath.PI) : (3.141592653589793D)) - d10;
} }
if (flag) { if (flag) {
@ -300,7 +310,7 @@ public class MathHelper {
for (i = 0; i < 257; ++i) { for (i = 0; i < 257; ++i) {
double d0 = (double) i / 256.0D; double d0 = (double) i / 256.0D;
double d1 = Math.asin(d0); double d1 = (fastMathMode ? (FastMath.asin(d0)) : (Math.asin(d0)));;
MathHelper.f[i] = Math.cos(d1); MathHelper.f[i] = Math.cos(d1);
MathHelper.e[i] = d1; MathHelper.e[i] = d1;

View File

@ -598,7 +598,7 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
} }
} }
public void stop() throws ExceptionWorldConflict { // CraftBukkit - added throws public void stop() throws ExceptionWorldConflict, InterruptedException { // CraftBukkit - added throws
// CraftBukkit start - prevent double stopping on multiple threads // CraftBukkit start - prevent double stopping on multiple threads
synchronized (stopLock) { synchronized (stopLock) {
if (hasStopped) return; if (hasStopped) return;
@ -615,7 +615,7 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
} }
// CraftBukkit end // CraftBukkit end
if (this.aq() != null) { if (this.aq() != null) {
this.aq().b(); this.aq().stopServer();
} }
if (this.v != null) { if (this.v != null) {
@ -997,7 +997,7 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
if (this.getPlayerList().getPlayerCount() != 0) // Tuinity if (this.getPlayerList().getPlayerCount() != 0) // Tuinity
{ {
// Tuinity start - controlled flush for entity tracker packets // Tuinity start - controlled flush for entity tracker packets
List<NetworkManager> disabledFlushes = new java.util.ArrayList<>(this.getPlayerList().getPlayerCount()); Set<NetworkManager> disabledFlushes = new HashSet<>(this.getPlayerList().getPlayerCount());
for (EntityPlayer player : this.getPlayerList().players) { for (EntityPlayer player : this.getPlayerList().players) {
PlayerConnection connection = player.playerConnection; PlayerConnection connection = player.playerConnection;
if (connection != null) { if (connection != null) {
@ -1406,6 +1406,8 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
public abstract boolean ai(); public abstract boolean ai();
public abstract ServerConnection.EventGroupType getTransport();
public boolean getPVP() { public boolean getPVP() {
return this.pvpMode; return this.pvpMode;
} }

View File

@ -1,69 +1,61 @@
package net.minecraft.server; package net.minecraft.server;
import com.elevatemc.spigot.eSpigot; import com.elevatemc.spigot.eSpigot;
import com.elevatemc.spigot.util.CryptException; import com.elevatemc.spigot.exception.CryptException;
import com.elevatemc.spigot.exception.ExploitException;
import com.velocitypowered.natives.compression.VelocityCompressor; // Paper
import com.velocitypowered.natives.util.Natives; // Paper
import com.google.common.collect.Queues; import com.google.common.collect.Queues;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.velocitypowered.natives.compression.VelocityCompressor;
import com.velocitypowered.natives.util.Natives;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*; import io.netty.channel.*;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.local.LocalChannel; import io.netty.channel.local.LocalChannel;
import io.netty.channel.local.LocalEventLoopGroup;
import io.netty.channel.local.LocalServerChannel; import io.netty.channel.local.LocalServerChannel;
import io.netty.channel.nio.NioEventLoopGroup; import io.netty.handler.codec.DecoderException;
import io.netty.handler.timeout.TimeoutException; import io.netty.handler.timeout.TimeoutException;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
import io.netty.util.concurrent.AbstractEventExecutor;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener; import io.netty.util.concurrent.GenericFutureListener;
import java.net.SocketAddress;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager; import org.apache.logging.log4j.MarkerManager;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import java.net.SocketAddress; public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class NetworkManager extends SimpleChannelInboundHandler<Packet> { private static final Logger LOGGER = LogManager.getLogger();
public static final Marker ROOT_MARKER = MarkerManager.getMarker("NETWORK");
public static final Marker PACKET_MARKER = MarkerManager.getMarker("NETWORK_PACKETS", NetworkManager.ROOT_MARKER);
public static final AttributeKey<EnumProtocol> ATTRIBUTE_PROTOCOL = AttributeKey.valueOf("protocol");
public static final AttributeKey<EnumProtocol> c = ATTRIBUTE_PROTOCOL;
public static final Marker a = MarkerManager.getMarker("NETWORK"); private final EnumProtocolDirection h;
public static final Marker b = MarkerManager.getMarker("NETWORK_PACKETS", NetworkManager.a); private final Queue<NetworkManager.QueuedPacket> i = Queues.newConcurrentLinkedQueue();
public static final AttributeKey<EnumProtocol> c = AttributeKey.valueOf("protocol"); private final ReentrantReadWriteLock j = new ReentrantReadWriteLock();
public static final LazyInitVar<NioEventLoopGroup> d = new LazyInitVar() { public Channel channel;
protected NioEventLoopGroup a() { // Spigot Start // PAIL
return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).build()); public SocketAddress l;
} public java.util.UUID spoofedUUID;
public com.mojang.authlib.properties.Property[] spoofedProfile;
public boolean preparing = true;
// Spigot End
private PacketListener m;
private IChatBaseComponent n;
private boolean encrypted; // Nacho - deobfuscate
private boolean isDisconnectionHandled; // Nacho - deobfuscate
protected Object init() { // eSpigot start - faster packets
return this.a(); private final Queue<NetworkManager.QueuedPacket> fastPackets = Queues.newConcurrentLinkedQueue();
}
};
public static final LazyInitVar<EpollEventLoopGroup> e = new LazyInitVar() {
protected EpollEventLoopGroup a() {
return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Client IO #%d").setDaemon(true).build());
}
protected Object init() {
return this.a();
}
};
public static final LazyInitVar<LocalEventLoopGroup> f = new LazyInitVar() {
protected LocalEventLoopGroup a() {
return new LocalEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).build());
}
protected Object init() {
return this.a();
}
};
private static final Logger g = LogManager.getLogger();
public static final Set<Class<? extends Packet<?>>> FAST_ELIGIBLE = new LinkedHashSet<>(); public static final Set<Class<? extends Packet<?>>> FAST_ELIGIBLE = new LinkedHashSet<>();
static { static {
@ -75,31 +67,13 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
FAST_ELIGIBLE.add(PacketPlayOutEntity.PacketPlayOutRelEntityMoveLook.class); FAST_ELIGIBLE.add(PacketPlayOutEntity.PacketPlayOutRelEntityMoveLook.class);
FAST_ELIGIBLE.add(PacketPlayOutAnimation.class); FAST_ELIGIBLE.add(PacketPlayOutAnimation.class);
} }
// eSpigot end
private final EnumProtocolDirection h;
private final ConcurrentLinkedQueue<QueuedPacket> i = Queues.newConcurrentLinkedQueue();
private final ConcurrentLinkedQueue<QueuedPacket> fastPackets = Queues.newConcurrentLinkedQueue(); // eSpigot - Process combat packets faster
private final ReentrantReadWriteLock j = new ReentrantReadWriteLock();
private final java.util.concurrent.atomic.AtomicInteger packetWrites = new java.util.concurrent.atomic.AtomicInteger();
private final Object flushLock = new Object();
public Channel channel;
// Spigot Start // PAIL
public SocketAddress l;
public java.util.UUID spoofedUUID;
public com.mojang.authlib.properties.Property[] spoofedProfile;
public boolean preparing = true;
// Tuinity start - allow controlled flushing // Tuinity start - allow controlled flushing
volatile boolean canFlush = true; volatile boolean canFlush = true;
// Spigot End private final java.util.concurrent.atomic.AtomicInteger packetWrites = new java.util.concurrent.atomic.AtomicInteger();
private PacketListener m;
private IChatBaseComponent n;
private boolean o;
private boolean p;
private int flushPacketsStart; private int flushPacketsStart;
private final Object flushLock = new Object();
public NetworkManager(EnumProtocolDirection enumprotocoldirection) {
this.h = enumprotocoldirection;
}
void disableAutomaticFlush() { void disableAutomaticFlush() {
synchronized (this.flushLock) { synchronized (this.flushLock) {
@ -109,56 +83,50 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
} }
void enableAutomaticFlush() { void enableAutomaticFlush() {
synchronized (this.flushLock) { synchronized (this.flushLock)
{
this.canFlush = true; this.canFlush = true;
if (this.packetWrites.get() != this.flushPacketsStart) // must be after canFlush = true if (this.packetWrites.get() != this.flushPacketsStart) { // must be after canFlush = true
this.flush(); // only make the flush call if we need to this.flush(); // only make the flush call if we need to
}
} }
} }
private void flush()
{
if (this.channel.eventLoop().inEventLoop()) {
this.channel.flush();
} //[Nacho-Spigot] Fixed RejectedExecutionException: event executor terminated by BeyazPolis
}
// Tuinity end - allow controlled flushing // Tuinity end - allow controlled flushing
private void flush() { public NetworkManager(EnumProtocolDirection enumprotocoldirection) {
if (this.channel.eventLoop().inEventLoop()) this.h = enumprotocoldirection;
this.channel.flush();
} }
public void channelActive(ChannelHandlerContext channelhandlercontext) throws Exception { public void channelActive(ChannelHandlerContext channelhandlercontext) throws Exception {
super.channelActive(channelhandlercontext); super.channelActive(channelhandlercontext);
this.channel = channelhandlercontext.channel(); this.channel = channelhandlercontext.channel();
this.l = this.channel.remoteAddress(); this.l = this.channel.remoteAddress();
// Spigot Start // Spigot Start
this.preparing = false; this.preparing = false;
// Spigot End // Spigot End
try { try {
this.a(EnumProtocol.HANDSHAKING); this.setProtocol(EnumProtocol.HANDSHAKING);
} catch (Throwable throwable) { } catch (Throwable throwable) {
NetworkManager.g.fatal(throwable); NetworkManager.LOGGER.fatal(throwable);
} }
} }
public void setupEncryption(javax.crypto.SecretKey key) throws CryptException { public void setProtocol(EnumProtocol protocol) {
if (!this.o) { a(protocol);
try {
com.velocitypowered.natives.encryption.VelocityCipher decryption = com.velocitypowered.natives.util.Natives.cipher.get().forDecryption(key);
com.velocitypowered.natives.encryption.VelocityCipher encryption = com.velocitypowered.natives.util.Natives.cipher.get().forEncryption(key);
this.o = true;
this.channel.pipeline().addBefore("splitter", "decrypt", new PacketDecrypter(decryption));
this.channel.pipeline().addBefore("prepender", "encrypt", new PacketEncrypter(encryption));
} catch (java.security.GeneralSecurityException e) {
throw new CryptException(e);
}
}
} }
// Paper end
public void a(EnumProtocol enumprotocol) { public void a(EnumProtocol protocol) {
this.channel.attr(NetworkManager.c).set(enumprotocol); this.channel.attr(NetworkManager.ATTRIBUTE_PROTOCOL).set(protocol);
this.channel.config().setAutoRead(true); this.channel.config().setAutoRead(true);
NetworkManager.g.debug("Enabled auto read");
} }
public void channelInactive(ChannelHandlerContext channelhandlercontext) throws Exception { public void channelInactive(ChannelHandlerContext channelhandlercontext) throws Exception {
@ -168,6 +136,20 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
public void exceptionCaught(ChannelHandlerContext channelhandlercontext, Throwable throwable) throws Exception { public void exceptionCaught(ChannelHandlerContext channelhandlercontext, Throwable throwable) throws Exception {
ChatMessage chatmessage; ChatMessage chatmessage;
if(throwable instanceof DecoderException) {
DecoderException decoderException = ((DecoderException) throwable);
if(decoderException.getCause() instanceof ExploitException) {
Bukkit.getLogger().warning("Server crash detected...");
if(this.getPacketListener() != null && this.getPacketListener() instanceof PlayerConnection) {
PlayerConnection playerConnection = (PlayerConnection) this.getPacketListener();
CraftPlayer player = playerConnection.getPlayer();
if(player != null) {
Bukkit.getLogger().warning(player.getName() + " has tried to crash the server... " + decoderException.getCause());
}
}
}
}
if (throwable instanceof TimeoutException) { if (throwable instanceof TimeoutException) {
chatmessage = new ChatMessage("disconnect.timeout"); chatmessage = new ChatMessage("disconnect.timeout");
} else { } else {
@ -175,7 +157,7 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
} }
this.close(chatmessage); this.close(chatmessage);
if (MinecraftServer.getServer().isDebugging()) throwable.printStackTrace(); // Spigot // if (MinecraftServer.getServer().isDebugging()) throwable.printStackTrace(); // Spigot
} }
protected void a(ChannelHandlerContext channelhandlercontext, Packet packet) throws Exception { protected void a(ChannelHandlerContext channelhandlercontext, Packet packet) throws Exception {
@ -183,7 +165,6 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
try { try {
packet.a(this.m); packet.a(this.m);
if (this.m instanceof PlayerConnection) { if (this.m instanceof PlayerConnection) {
try { try {
eSpigot.getInstance().getPacketHandlers().forEach(packetHandler -> packetHandler.handleReceivedPacket((PlayerConnection) this.m, packet)); eSpigot.getInstance().getPacketHandlers().forEach(packetHandler -> packetHandler.handleReceivedPacket((PlayerConnection) this.m, packet));
@ -199,38 +180,20 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
public void a(PacketListener packetlistener) { public void a(PacketListener packetlistener) {
Validate.notNull(packetlistener, "packetListener"); Validate.notNull(packetlistener, "packetListener");
NetworkManager.g.debug("Set listener of {} to {}", this, packetlistener); // NetworkManager.g.debug("Set listener of {} to {}", new Object[] { this, packetlistener});
this.m = packetlistener; this.m = packetlistener;
} }
//sendPacket
public void handle(Packet packet) { public void handle(Packet packet) {
if (this.g()) { if (this.isConnected()) {
this.m(); this.sendPacketQueue();
this.a(packet, null); this.dispatchPacket(packet, null, Boolean.TRUE);
} else { // Channel closed
this.j.writeLock().lock();
try {
this.i.add(new NetworkManager.QueuedPacket(packet, (GenericFutureListener[]) null));
} finally {
this.j.writeLock().unlock();
}
}
}
public void a(Packet packet, GenericFutureListener<? extends Future<? super Void>> genericfuturelistener, GenericFutureListener<? extends Future<? super Void>>... agenericfuturelistener) {
if (this.g()) {
this.m();
this.a(packet, ArrayUtils.add(agenericfuturelistener, 0, genericfuturelistener), Boolean.TRUE);
} else { } else {
this.j.writeLock().lock(); this.j.writeLock().lock();
try { try {
if (FAST_ELIGIBLE.contains(packet.getClass())) this.i.add(new NetworkManager.QueuedPacket(packet));
this.fastPackets.add(new NetworkManager.QueuedPacket(packet, ArrayUtils.add(agenericfuturelistener, 0, genericfuturelistener)));
else
this.i.add(new NetworkManager.QueuedPacket(packet, ArrayUtils.add(agenericfuturelistener, 0, genericfuturelistener)));
} finally { } finally {
this.j.writeLock().unlock(); this.j.writeLock().unlock();
} }
@ -238,104 +201,110 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
} }
private void a(final Packet packet, final GenericFutureListener<? extends Future<? super Void>>[] agenericfuturelistener, Boolean shouldFlush) { //sendPacket
this.packetWrites.getAndIncrement(); // must be before using canFlush public void a(Packet packet, GenericFutureListener<? extends Future<? super Void>> listener, GenericFutureListener<? extends Future<? super Void>>... listeners) {
boolean effectiveFlush = shouldFlush == null ? this.canFlush : shouldFlush; if (this.isConnected()) {
final boolean flush = effectiveFlush this.sendPacketQueue();
|| packet instanceof PacketPlayOutKeepAlive this.dispatchPacket(packet, ArrayUtils.insert(0, listeners, listener), Boolean.TRUE);
|| packet instanceof PacketPlayOutKickDisconnect } else {
|| FAST_ELIGIBLE.contains(packet.getClass()); this.j.writeLock().lock();
final EnumProtocol enumprotocol = EnumProtocol.a(packet);
final EnumProtocol enumprotocol1 = this.channel.attr(NetworkManager.c).get();
try {
if (FAST_ELIGIBLE.contains(packet.getClass())) {
this.fastPackets.add(new NetworkManager.QueuedPacket(packet, ArrayUtils.insert(0, listeners, listener)));
} else {
this.i.add(new NetworkManager.QueuedPacket(packet, ArrayUtils.insert(0, listeners, listener)));
}
} finally {
this.j.writeLock().unlock();
}
}
}
// Paper / Nacho start
public EntityPlayer getPlayer() {
if (getPacketListener() instanceof PlayerConnection) {
return ((PlayerConnection) getPacketListener()).player;
} else {
return null;
}
}
// Paper / Nacho end
public void dispatchPacket(final Packet<?> packet, final GenericFutureListener<? extends Future<? super Void>>[] listeners, Boolean flushConditional) {
this.packetWrites.getAndIncrement(); // must be before using canFlush
boolean effectiveFlush = flushConditional == null ? this.canFlush : flushConditional;
final boolean flush = effectiveFlush || packet instanceof PacketPlayOutKeepAlive || packet instanceof PacketPlayOutKickDisconnect || FAST_ELIGIBLE.contains(packet.getClass()); // no delay for certain packets
final EnumProtocol enumprotocol = EnumProtocol.getProtocolForPacket(packet);
final EnumProtocol enumprotocol1 = this.channel.attr(NetworkManager.ATTRIBUTE_PROTOCOL).get();
if (enumprotocol1 != enumprotocol) { if (enumprotocol1 != enumprotocol) {
NetworkManager.g.debug("Disabled auto read");
this.channel.config().setAutoRead(false); this.channel.config().setAutoRead(false);
} }
if (this.channel.eventLoop().inEventLoop()) { if (this.channel.eventLoop().inEventLoop()) {
if (enumprotocol != enumprotocol1) { if (enumprotocol != enumprotocol1) {
this.a(enumprotocol); this.setProtocol(enumprotocol);
} }
ChannelFuture channelfuture = flush ? this.channel.writeAndFlush(packet) : this.channel.write(packet); ChannelFuture channelfuture = flush ? this.channel.writeAndFlush(packet) : this.channel.write(packet);
if (listeners != null) {
if (agenericfuturelistener != null) channelfuture.addListeners(listeners);
channelfuture.addListeners(agenericfuturelistener); }
channelfuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); channelfuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
} else { }
Runnable command = new Runnable() { // PandaSpigot - optimize packets that are not flushed else {
public void run() { // Tuinity start - optimise packets that are not flushed
Runnable choice1 = null;
AbstractEventExecutor.LazyRunnable choice2 = null;
// note: since the type is not dynamic here, we need to actually copy the old executor code
// into two branches. On conflict, just re-copy - no changes were made inside the executor code.
if (flush) {
choice1 = () -> {
if (enumprotocol != enumprotocol1) { if (enumprotocol != enumprotocol1) {
NetworkManager.this.a(enumprotocol); this.setProtocol(enumprotocol);
} }
try { try {
ChannelFuture channelfuture1 = (flush) ? NetworkManager.this.channel.writeAndFlush(packet) : NetworkManager.this.channel.write(packet); // PandaSpigot - add flush parameter ChannelFuture channelfuture1 = (flush) ? this.channel.writeAndFlush(packet) : this.channel.write(packet); // Tuinity - add flush parameter
if (agenericfuturelistener != null) if (listeners != null) {
channelfuture1.addListeners(agenericfuturelistener); channelfuture1.addListeners(listeners);
}
channelfuture1.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); channelfuture1.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
} catch (Exception e) { } catch (Exception e) {
g.error("NetworkException: ", e); LOGGER.error("NetworkException: " + getPlayer(), e);
close(new ChatMessage("disconnect.genericReason", "Internal Exception: " + e.getMessage())); close(new ChatMessage("disconnect.genericReason", "Internal Exception: " + e.getMessage()));;
} }
} };
// PandaSpigot start - optimize packets that are not flushed
};
if (!flush) {
// create a LazyRunnable that when executed, calls command.run()
io.netty.util.concurrent.AbstractEventExecutor.LazyRunnable run = command::run;
this.channel.eventLoop().execute(run);
} else { } else {
// if flushing, just schedule like normal // explicitly declare a variable to make the lambda use the type
this.channel.eventLoop().execute(command); choice2 = () -> {
} if (enumprotocol != enumprotocol1) {
// PandaSpigot end this.setProtocol(enumprotocol);
}
}
private final Object fastPacketsLock = new Object();
private void m() {
if (this.i.isEmpty()) // Don't lock
return;
if (this.channel != null && this.channel.isOpen()) {
this.j.readLock().lock();
boolean needsFlush = this.canFlush;
boolean hasWrotePacket = false;
try {
Iterator<QueuedPacket> iterator = this.i.iterator();
while (iterator.hasNext()) {
QueuedPacket queued = iterator.next();
Packet<?> packet = queued.a;
if (hasWrotePacket && (needsFlush || this.canFlush)) flush();
iterator.remove();
this.a(packet, queued.b, (!iterator.hasNext() && (needsFlush || this.canFlush)) ? Boolean.TRUE : Boolean.FALSE);
hasWrotePacket = true;
}
if (!fastPackets.isEmpty()) {
synchronized (fastPacketsLock) {
iterator = this.fastPackets.iterator();
while (iterator.hasNext()) {
QueuedPacket queued = iterator.next();
Packet<?> packet = queued.a;
if (hasWrotePacket && (needsFlush || this.canFlush)) flush();
iterator.remove();
this.a(packet, queued.b, (!iterator.hasNext() && (needsFlush || this.canFlush)) ? Boolean.TRUE : Boolean.FALSE);
hasWrotePacket = true;
}
} }
} try {
} finally { // Nacho - why not remove the check below if the check is done above? just code duplication...
this.j.readLock().unlock(); // even IntelliJ screamed at me for doing leaving it like that :shrug:
ChannelFuture channelfuture1 = /* (flush) ? this.channel.writeAndFlush(packet) : */this.channel.write(packet); // Nacho - see above // Tuinity - add flush parameter
if (listeners != null) {
channelfuture1.addListeners(listeners);
}
channelfuture1.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
} catch (Exception e) {
LOGGER.error("NetworkException: " + getPlayer(), e);
close(new ChatMessage("disconnect.genericReason", "Internal Exception: " + e.getMessage()));;
}
};
} }
this.channel.eventLoop().execute(choice1 != null ? choice1 : choice2);
// Tuinity end - optimise packets that are not flushed
} }
} }
private void a(final Packet packet, final GenericFutureListener<? extends Future<? super Void>>[] agenericfuturelistener) {
this.dispatchPacket(packet, agenericfuturelistener, Boolean.TRUE);
}
// eSpigot start - fast packets processing
private final Object fastPacketsLock = new Object();
public void processFastPackets() { public void processFastPackets() {
if (this.fastPackets.isEmpty()) // Don't lock if (this.fastPackets.isEmpty()) // Don't lock
return; return;
@ -349,10 +318,10 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
Iterator<QueuedPacket> iterator = this.fastPackets.iterator(); Iterator<QueuedPacket> iterator = this.fastPackets.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
QueuedPacket queued = iterator.next(); QueuedPacket queued = iterator.next();
Packet<?> packet = queued.a; Packet packet = queued.a;
if (hasWrotePacket && (needsFlush || this.canFlush)) flush(); if (hasWrotePacket && (needsFlush || this.canFlush)) flush();
iterator.remove(); iterator.remove();
this.a(packet, queued.b, (!iterator.hasNext() && (needsFlush || this.canFlush)) ? Boolean.TRUE : Boolean.FALSE); this.dispatchPacket(packet, queued.b, (!iterator.hasNext() && (needsFlush || this.canFlush)) ? Boolean.TRUE : Boolean.FALSE);
hasWrotePacket = true; hasWrotePacket = true;
} }
} finally { } finally {
@ -361,9 +330,37 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
} }
} }
} }
// eSpigot end
public void a() { private void sendPacketQueue() {
this.m(); if(this.i.isEmpty()) return; // [Nacho-0019] :: Avoid lock every packet send
if (this.channel != null && this.channel.isOpen()) {
this.j.readLock().lock();
boolean needsFlush = this.canFlush;
boolean hasWrotePacket = false;
try {
Iterator<QueuedPacket> iterator = this.i.iterator();
while (iterator.hasNext()) {
QueuedPacket queued = iterator.next();
Packet packet = queued.a;
if (hasWrotePacket && (needsFlush || this.canFlush)) flush();
iterator.remove();
this.dispatchPacket(packet, queued.b, (!iterator.hasNext() && (needsFlush || this.canFlush)) ? Boolean.TRUE : Boolean.FALSE);
hasWrotePacket = true;
}
} finally {
this.j.readLock().unlock();
}
}
}
private void m()
{
this.sendPacketQueue();
}
public void tick() {
this.sendPacketQueue();
if (this.m instanceof IUpdatePlayerListBox) { if (this.m instanceof IUpdatePlayerListBox) {
((IUpdatePlayerListBox) this.m).c(); ((IUpdatePlayerListBox) this.m).c();
} }
@ -371,12 +368,16 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
this.channel.flush(); this.channel.flush();
} }
public void a() {
this.tick();
}
public SocketAddress getSocketAddress() { public SocketAddress getSocketAddress() {
return this.l; return this.l;
} }
public void close(IChatBaseComponent ichatbasecomponent) { public void close(IChatBaseComponent ichatbasecomponent) {
this.i.clear(); // KigPaper this.i.clear(); // FlamePaper - Minetick fix memory leaks
// Spigot Start // Spigot Start
this.preparing = false; this.preparing = false;
// Spigot End // Spigot End
@ -384,21 +385,43 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
this.channel.close(); // We can't wait as this may be called from an event loop. this.channel.close(); // We can't wait as this may be called from an event loop.
this.n = ichatbasecomponent; this.n = ichatbasecomponent;
} }
} }
public boolean c() { public boolean c() {
return this.channel instanceof LocalChannel || this.channel instanceof LocalServerChannel; return this.channel instanceof LocalChannel || this.channel instanceof LocalServerChannel;
} }
/* public void a(SecretKey secretkey) { // Paper start
/*
public void setEncryptionKey(SecretKey secretkey) {
this.o = true; this.o = true;
this.channel.pipeline().addBefore("splitter", "decrypt", new PacketDecrypter(MinecraftEncryption.a(2, secretkey))); this.channel.pipeline().addBefore("splitter", "decrypt", new PacketDecrypter(MinecraftEncryption.a(2, secretkey)));
this.channel.pipeline().addBefore("prepender", "encrypt", new PacketEncrypter(MinecraftEncryption.a(1, secretkey))); this.channel.pipeline().addBefore("prepender", "encrypt", new PacketEncrypter(MinecraftEncryption.a(1, secretkey)));
} */ }*/
public void setupEncryption(javax.crypto.SecretKey key) throws CryptException {
if (!this.encrypted) { // Nacho - deobfuscate encrypted
try {
com.velocitypowered.natives.encryption.VelocityCipher decryption = com.velocitypowered.natives.util.Natives.cipher.get().forDecryption(key);
com.velocitypowered.natives.encryption.VelocityCipher encryption = com.velocitypowered.natives.util.Natives.cipher.get().forEncryption(key);
this.encrypted = true; // Nacho - deobfuscate encrypted
this.channel.pipeline().addBefore("splitter", "decrypt", new PacketDecrypter(decryption));
this.channel.pipeline().addBefore("prepender", "encrypt", new PacketEncrypter(encryption));
} catch (java.security.GeneralSecurityException e) {
throw new CryptException(e);
}
}
}
// Paper end
public boolean isConnected()
{
return this.channel != null && this.channel.isOpen();
}
public boolean g() { public boolean g() {
return this.channel != null && this.channel.isOpen(); return this.isConnected();
} }
public boolean h() { public boolean h() {
@ -417,25 +440,17 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
this.channel.config().setAutoRead(false); this.channel.config().setAutoRead(false);
} }
public void a(int i) public void setupCompression(int compressionThreshold) { // Nacho - deobfuscate
{ if (compressionThreshold >= 0) {
// Nacho start - OBFHELPER
this.setupCompression(i);
}
public void setupCompression(int compressionThreshold) {
// Nacho end
if (compressionThreshold >= 0)
{
VelocityCompressor compressor = Natives.compress.get().create(-1); // Paper VelocityCompressor compressor = Natives.compress.get().create(-1); // Paper
if (this.channel.pipeline().get("decompress") instanceof PacketDecompressor) { if (this.channel.pipeline().get("decompress") instanceof PacketDecompressor) {
((PacketDecompressor) this.channel.pipeline().get("decompress")).a(compressionThreshold); ((PacketDecompressor) this.channel.pipeline().get("decompress")).setThreshold(compressionThreshold); // Nacho - deobfuscate setThreshold
} else { } else {
this.channel.pipeline().addBefore("decoder", "decompress", new PacketDecompressor(compressor, compressionThreshold)); // Paper this.channel.pipeline().addBefore("decoder", "decompress", new PacketDecompressor(compressor, compressionThreshold)); // Paper
} }
if (this.channel.pipeline().get("compress") instanceof PacketCompressor) { if (this.channel.pipeline().get("compress") instanceof PacketCompressor) {
((PacketCompressor) this.channel.pipeline().get("decompress")).a(compressionThreshold); ((PacketCompressor) this.channel.pipeline().get("decompress")).setThreshold(compressionThreshold); // Nacho - deobfuscate setThreshold
} else { } else {
this.channel.pipeline().addBefore("encoder", "compress", new PacketCompressor(compressor, compressionThreshold)); // Paper this.channel.pipeline().addBefore("encoder", "compress", new PacketCompressor(compressor, compressionThreshold)); // Paper
} }
@ -450,10 +465,13 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
} }
} }
public void l() { public void handleDisconnection()
if (this.channel != null && !this.channel.isOpen()) { {
if (!this.p) { if (this.channel != null && !this.channel.isOpen())
this.p = true; {
if (!this.isDisconnectionHandled) // Nacho - deobfuscate isDisconnectionHandled
{
this.isDisconnectionHandled = true; // Nacho - deobfuscate isDisconnectionHandled
if (this.j() != null) { if (this.j() != null) {
this.getPacketListener().a(this.j()); this.getPacketListener().a(this.j());
} else if (this.getPacketListener() != null) { } else if (this.getPacketListener() != null) {
@ -461,33 +479,40 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
} }
this.i.clear(); // Free up packet queue. this.i.clear(); // Free up packet queue.
} else { } else {
NetworkManager.g.warn("handleDisconnection() called twice"); NetworkManager.LOGGER.warn("handleDisconnection() called twice");
} }
} }
} }
public void l()
{
this.handleDisconnection();
}
@Override
protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet object) throws Exception { // CraftBukkit - fix decompile error protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet object) throws Exception { // CraftBukkit - fix decompile error
// this.a(channelhandlercontext, object);
// FlamePaper - Check if channel is opened before reading packet // FlamePaper - Check if channel is opened before reading packet
if (g()) { if (g()) {
this.a(channelhandlercontext, object); this.a(channelhandlercontext, object);
} }
} }
static class QueuedPacket {
private final Packet a; //packet
private final GenericFutureListener<? extends Future<? super Void>>[] b; //listener
@SafeVarargs
public QueuedPacket(Packet packet, GenericFutureListener<? extends Future<? super Void>> ...listeners) {
this.a = packet;
this.b = listeners;
}
}
// Spigot Start // Spigot Start
public SocketAddress getRawAddress() { public SocketAddress getRawAddress() {
return this.channel.remoteAddress(); return this.channel.remoteAddress();
} }
static class QueuedPacket {
private final Packet a;
private final GenericFutureListener<? extends Future<? super Void>>[] b;
public QueuedPacket(Packet packet, GenericFutureListener<? extends Future<? super Void>>... agenericfuturelistener) {
this.a = packet;
this.b = agenericfuturelistener;
}
}
// Spigot End // Spigot End
} }

View File

@ -13,33 +13,30 @@ import org.apache.logging.log4j.MarkerManager;
public class PacketDecoder extends ByteToMessageDecoder { public class PacketDecoder extends ByteToMessageDecoder {
private static final Logger a = LogManager.getLogger(); private static final Logger a = LogManager.getLogger();
private static final Marker b = MarkerManager.getMarker("PACKET_RECEIVED", NetworkManager.b); private static final Marker b;
private final EnumProtocolDirection c; private final EnumProtocolDirection c;
public PacketDecoder(EnumProtocolDirection enumprotocoldirection) { public PacketDecoder(EnumProtocolDirection enumprotocoldirection) {
this.c = enumprotocoldirection; this.c = enumprotocoldirection;
} }
protected void decode(ChannelHandlerContext channelhandlercontext, ByteBuf bytebuf, List<Object> list) throws Exception { protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> list) throws Exception {
if (bytebuf.readableBytes() != 0) { if (!bytebuf.isReadable()) return;
PacketDataSerializer packetdataserializer = new PacketDataSerializer(bytebuf);
int i = packetdataserializer.e();
Packet packet = ((EnumProtocol) channelhandlercontext.channel().attr(NetworkManager.c).get()).a(this.c, i);
if (packet == null) { PacketDataSerializer packetDataHelper = new PacketDataSerializer(bytebuf);
throw new IOException("Bad packet id " + i); int packetId = packetDataHelper.e();
} else { Packet<?> packet = ctx.channel().attr(NetworkManager.ATTRIBUTE_PROTOCOL).get().a(this.c, packetId);
packet.a(packetdataserializer); if (packet == null)
if (packetdataserializer.readableBytes() > 0) { throw new IOException("Bad packet id " + packetId);
throw new IOException("Packet " + ((EnumProtocol) channelhandlercontext.channel().attr(NetworkManager.c).get()).a() + "/" + i + " (" + packet.getClass().getSimpleName() + ") was larger than I expected, found " + packetdataserializer.readableBytes() + " bytes extra whilst reading packet " + i);
} else {
list.add(packet);
if (PacketDecoder.a.isDebugEnabled()) {
PacketDecoder.a.debug(PacketDecoder.b, " IN: [{}:{}] {}", new Object[] { channelhandlercontext.channel().attr(NetworkManager.c).get(), Integer.valueOf(i), packet.getClass().getName()});
}
} packet.a(packetDataHelper);
}
} if (packetDataHelper.isReadable())
throw new IOException("Packet " + ctx.channel().attr(NetworkManager.ATTRIBUTE_PROTOCOL).get().getStateId() + "/" + packetId + " (" + packet.getClass().getSimpleName() + ") was larger than I expected, found " + packetDataHelper.readableBytes() + " bytes extra whilst reading packet " + packetId);
list.add(packet);
}
static {
b = MarkerManager.getMarker("PACKET_RECEIVED", NetworkManager.PACKET_MARKER);
} }
} }

View File

@ -78,4 +78,8 @@ public class PacketDecompressor extends ByteToMessageDecoder {
public void a(int var1) { public void a(int var1) {
this.threshold = var1; this.threshold = var1;
} }
public void setThreshold(int var1) { // Nacho - deobfuscate
a(var1);
}
} }

View File

@ -1,52 +1,44 @@
package net.minecraft.server; package net.minecraft.server;
import com.elevatemc.spigot.exception.ExploitException;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder; import io.netty.handler.codec.MessageToByteEncoder;
import java.io.IOException; import java.io.IOException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
public class PacketEncoder extends MessageToByteEncoder<Packet> { public class PacketEncoder extends MessageToByteEncoder<Packet<?>> {
private static final Logger a = LogManager.getLogger(); // private static final Logger a = LogManager.getLogger();
private static final Marker b = MarkerManager.getMarker("PACKET_SENT", NetworkManager.b); // private static final Marker b = MarkerManager.getMarker("PACKET_SENT", NetworkManager.PACKET_MARKER);
private final EnumProtocolDirection c; private final EnumProtocolDirection c;
public PacketEncoder(EnumProtocolDirection enumprotocoldirection) { public PacketEncoder(EnumProtocolDirection enumprotocoldirection) {
this.c = enumprotocoldirection; this.c = enumprotocoldirection;
} }
protected void a(ChannelHandlerContext channelhandlercontext, Packet packet, ByteBuf bytebuf) throws Exception { protected void a(ChannelHandlerContext ctx, Packet<?> packet, ByteBuf bytebuf) throws Exception {
Integer integer = ((EnumProtocol) channelhandlercontext.channel().attr(NetworkManager.c).get()).a(this.c, packet); Integer packetId = (ctx.channel().attr(NetworkManager.c).get()).a(this.c, packet);
if (PacketEncoder.a.isDebugEnabled()) { /*if (PacketEncoder.a.isDebugEnabled()) {
PacketEncoder.a.debug(PacketEncoder.b, "OUT: [{}:{}] {}", new Object[] { channelhandlercontext.channel().attr(NetworkManager.c).get(), integer, packet.getClass().getName()}); PacketEncoder.a.debug(PacketEncoder.b, "OUT: [{}:{}] {}", ctx.channel().attr(NetworkManager.ATTRIBUTE_PROTOCOL).get(), packetId, packet.getClass().getName());
} }*/
if (integer == null) { if (packetId == null) {
throw new IOException("Can\'t serialize unregistered packet"); throw new IOException("Can't serialize unregistered packet");
} else { } else {
PacketDataSerializer packetdataserializer = new PacketDataSerializer(bytebuf); PacketDataSerializer serializer = new PacketDataSerializer(bytebuf);
serializer.b(packetId); // Nacho - deobfuscate writeVarInt
packetdataserializer.b(integer.intValue());
try { try {
if (packet instanceof PacketPlayOutNamedEntitySpawn) { packet.b(serializer);
packet = packet; } catch (ExploitException ex) {
} MinecraftServer.LOGGER.error("Exploit exception: " + ctx.channel().attr(NetworkManager.ATTRIBUTE_PROTOCOL).get());
packet.b(packetdataserializer);
} catch (Throwable throwable) {
PacketEncoder.a.error(throwable);
} }
} }
} }
protected void encode(ChannelHandlerContext channelhandlercontext, Packet object, ByteBuf bytebuf) throws Exception { @Override
this.a(channelhandlercontext, object, bytebuf); protected void encode(ChannelHandlerContext channelHandlerContext, Packet packet, ByteBuf byteBuf) throws Exception {
this.a(channelHandlerContext, packet, byteBuf);
} }
} }

View File

@ -53,7 +53,6 @@ public abstract class PlayerList {
private final GameProfileBanList k; private final GameProfileBanList k;
private final IpBanList l; private final IpBanList l;
private final OpList operators; private final OpList operators;
private Set<UUID> fastOperator = new HashSet<>();
private final WhiteList whitelist; private final WhiteList whitelist;
private final Map<UUID, ServerStatisticManager> o; private final Map<UUID, ServerStatisticManager> o;
public IPlayerFileData playerFileData; public IPlayerFileData playerFileData;
@ -76,9 +75,6 @@ public abstract class PlayerList {
this.k = new GameProfileBanList(PlayerList.a); this.k = new GameProfileBanList(PlayerList.a);
this.l = new IpBanList(PlayerList.b); this.l = new IpBanList(PlayerList.b);
this.operators = new OpList(PlayerList.c); this.operators = new OpList(PlayerList.c);
for (OpListEntry value : this.operators.getValues()) {
this.fastOperator.add(value.getKey().getId());
}
this.whitelist = new WhiteList(PlayerList.d); this.whitelist = new WhiteList(PlayerList.d);
this.o = Maps.newHashMap(); this.o = Maps.newHashMap();
this.server = minecraftserver; this.server = minecraftserver;
@ -1020,7 +1016,6 @@ public abstract class PlayerList {
public void addOp(GameProfile gameprofile) { public void addOp(GameProfile gameprofile) {
this.operators.add(new OpListEntry(gameprofile, this.server.p(), this.operators.b(gameprofile))); this.operators.add(new OpListEntry(gameprofile, this.server.p(), this.operators.b(gameprofile)));
this.fastOperator.add(gameprofile.getId());
// CraftBukkit start // CraftBukkit start
Player player = server.server.getPlayer(gameprofile.getId()); Player player = server.server.getPlayer(gameprofile.getId());
@ -1032,7 +1027,6 @@ public abstract class PlayerList {
public void removeOp(GameProfile gameprofile) { public void removeOp(GameProfile gameprofile) {
this.operators.remove(gameprofile); this.operators.remove(gameprofile);
this.fastOperator.remove(gameprofile.getId());
// CraftBukkit start // CraftBukkit start
Player player = server.server.getPlayer(gameprofile.getId()); Player player = server.server.getPlayer(gameprofile.getId());
@ -1043,11 +1037,11 @@ public abstract class PlayerList {
} }
public boolean isWhitelisted(GameProfile gameprofile) { public boolean isWhitelisted(GameProfile gameprofile) {
return !this.hasWhitelist || this.fastOperator.contains(gameprofile.getId()) || this.whitelist.d(gameprofile); return !this.hasWhitelist || this.operators.d(gameprofile) || this.whitelist.d(gameprofile);
} }
public boolean isOp(GameProfile gameprofile) { public boolean isOp(GameProfile gameprofile) {
return this.fastOperator.contains(gameprofile.getId()) || this.server.T() && this.server.worlds.get(0).getWorldData().v() && this.server.S().equalsIgnoreCase(gameprofile.getName()) || this.t; // CraftBukkit return this.operators.d(gameprofile) || this.server.T() && this.server.worlds.get(0).getWorldData().v() && this.server.S().equalsIgnoreCase(gameprofile.getName()) || this.t; // CraftBukkit
} }
public EntityPlayer getPlayer(String s) { public EntityPlayer getPlayer(String s) {

View File

@ -1,197 +1,189 @@
package net.minecraft.server; package net.minecraft.server;
import com.elevatemc.spigot.network.MinecraftPipeline;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.velocitypowered.natives.util.Natives; // Paper
import com.velocitypowered.natives.util.Natives;
import io.netty.bootstrap.ServerBootstrap; import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*; import io.netty.channel.*;
import io.netty.channel.epoll.Epoll; import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup; import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel; import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.local.LocalEventLoopGroup; import io.netty.channel.kqueue.KQueue;
import io.netty.channel.kqueue.KQueueEventLoopGroup;
import io.netty.channel.kqueue.KQueueServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener; import io.netty.util.concurrent.GenericFutureListener;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.ArrayList; import java.net.SocketAddress;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.github.paperspigot.PaperSpigotConfig;
public class ServerConnection { public class ServerConnection {
private static final Logger e = LogManager.getLogger(); public enum EventGroupType {
public static final LazyInitVar<NioEventLoopGroup> a = new LazyInitVar() { EPOLL,
protected NioEventLoopGroup a() { KQUEUE,
return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Server IO #%d").setDaemon(true).build()); NIO,
} DEFAULT
protected Object init() {
return this.a();
}
};
public static final LazyInitVar<EpollEventLoopGroup> b = new LazyInitVar() {
protected EpollEventLoopGroup a() {
return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Server IO #%d").setDaemon(true).build());
}
protected Object init() {
return this.a();
}
};
public static final LazyInitVar<LocalEventLoopGroup> c = new LazyInitVar() {
protected LocalEventLoopGroup a() {
return new LocalEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Server IO #%d").setDaemon(true).build());
}
protected Object init() {
return this.a();
}
};
private final MinecraftServer f;
public volatile boolean d;
private final List<ChannelFuture> g = Collections.synchronizedList(Lists.<ChannelFuture>newArrayList());
private final List<NetworkManager> h = Collections.synchronizedList(Lists.<NetworkManager>newArrayList());
public ServerConnection(MinecraftServer minecraftserver) {
this.f = minecraftserver;
this.d = true;
} }
private static final WriteBufferWaterMark SERVER_WRITE_MARK = new WriteBufferWaterMark(1 << 20, 1 << 21);
private static final Logger LOGGER = LogManager.getLogger();
private final EventGroupType eventGroupType;
public static EventLoopGroup boss, worker;
public final MinecraftServer server;
public volatile boolean started;
private final List<ChannelFuture> listeningChannels = Collections.synchronizedList(Lists.newArrayList());
private final List<NetworkManager> connectedChannels = Collections.synchronizedList(Lists.newArrayList());
// Paper start - prevent blocking on adding a new network manager while the server is ticking // Paper start - prevent blocking on adding a new network manager while the server is ticking
private final List<NetworkManager> pending = Collections.synchronizedList(Lists.newArrayList()); public final java.util.Queue<NetworkManager> pending = new java.util.concurrent.ConcurrentLinkedQueue<>();
private void addPending() { private void addPending() {
synchronized (pending) { NetworkManager manager;
this.h.addAll(pending); while ((manager = pending.poll()) != null) {
pending.clear(); this.connectedChannels.add(manager); // Nacho - deobfuscate connectedChannels
} }
} }
// Paper end // Paper end
// PandaSpigot start
public void a(InetAddress inetaddress, int i) throws IOException { public ServerConnection(MinecraftServer server) {
bind(new java.net.InetSocketAddress(inetaddress, i)); this.server = server;
this.started = true;
if (server.ai()) /* use-native-transport */ {
if (Epoll.isAvailable()) {
this.eventGroupType = EventGroupType.EPOLL;
return;
} else if (KQueue.isAvailable()) {
this.eventGroupType = EventGroupType.KQUEUE;
return;
}
}
this.eventGroupType = server.getTransport();
} }
public void bind(java.net.SocketAddress address) throws IOException {
// PandaSpigot end
List list = this.g;
synchronized (this.g) { public void a(SocketAddress ip, int port) throws IOException {
Class oclass; synchronized (this.listeningChannels) { // Nacho - deobfuscate listeningChannels
LazyInitVar lazyinitvar; Class<? extends ServerChannel> channel = null;
final int workerThreadCount = Runtime.getRuntime().availableProcessors();
try { {
if (Epoll.isAvailable() && this.f.ai()) { switch (eventGroupType) {
// PandaSpigot start - Unix domain socket support default:
if (address instanceof io.netty.channel.unix.DomainSocketAddress) { case DEFAULT: {
oclass = io.netty.channel.epoll.EpollServerDomainSocketChannel.class; LOGGER.info("Finding best event group type using fall-through");
} else { }
oclass = EpollServerSocketChannel.class;
case EPOLL: {
if (Epoll.isAvailable()) {
boss = new EpollEventLoopGroup(0);
worker = new EpollEventLoopGroup(workerThreadCount);
// PandaSpigot start - Unix domain socket support
if (ip instanceof io.netty.channel.unix.DomainSocketAddress) {
channel = io.netty.channel.epoll.EpollServerDomainSocketChannel.class;
} else {
channel = EpollServerSocketChannel.class;
}
LOGGER.info("Using epoll");
break;
}
}
case KQUEUE: {
if (KQueue.isAvailable()) {
boss = new KQueueEventLoopGroup(0);
worker = new KQueueEventLoopGroup(workerThreadCount);
channel = KQueueServerSocketChannel.class;
LOGGER.info("Using kqueue");
break;
}
}
case NIO: {
boss = new NioEventLoopGroup(0);
worker = new NioEventLoopGroup(workerThreadCount);
channel = NioServerSocketChannel.class;
LOGGER.info("Using NIO");
break;
} }
// PandaSpigot end
lazyinitvar = ServerConnection.b;
ServerConnection.e.info("Using epoll channel type");
} else {
oclass = NioServerSocketChannel.class;
lazyinitvar = ServerConnection.a;
ServerConnection.e.info("Using default channel type");
} }
} catch (Exception e) {
ServerConnection.e.warn("An error occurred trying to check if Epoll is available, falling back to default channel type.");
oclass = NioServerSocketChannel.class;
lazyinitvar = ServerConnection.a;
ServerConnection.e.info("Using default channel type");
} }
// Paper start - indicate Velocity natives in use // Paper/Nacho start - indicate Velocity natives in use
MinecraftServer.LOGGER.info("Using " + Natives.compress.getLoadedVariant() + " compression from Velocity."); LOGGER.info("Using " + Natives.compress.getLoadedVariant() + " compression from Velocity.");
MinecraftServer.LOGGER.info("Using " + Natives.cipher.getLoadedVariant() + " cipher from Velocity."); LOGGER.info("Using " + Natives.cipher.getLoadedVariant() + " cipher from Velocity.");
// Paper end // Paper/Nacho end
this.g.add((new ServerBootstrap()).channel(oclass).childHandler(new ChannelInitializer() { this.listeningChannels.add(((new ServerBootstrap() // Nacho - deobfuscate listeningChannels
protected void initChannel(Channel channel) throws Exception { .channel(channel))
// KigPaper start .childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, SERVER_WRITE_MARK)
if(PaperSpigotConfig.nettyReadTimeout) channel.pipeline().addLast("timeout", new ReadTimeoutHandler(30)); .childHandler(new MinecraftPipeline(this))
// KigPaper end .group(boss, worker)
// PandaSpigot start - newlines .localAddress(ip))
channel.pipeline() .bind()
.addLast("legacy_query", new LegacyPingHandler(ServerConnection.this)) .syncUninterruptibly());
.addLast("splitter", new PacketSplitter())
.addLast("decoder", new PacketDecoder(EnumProtocolDirection.SERVERBOUND))
.addLast("prepender", PacketPrepender.INSTANCE) // PandaSpigot - Share PacketPrepender instance
.addLast("encoder", new PacketEncoder(EnumProtocolDirection.CLIENTBOUND));
// PandaSpigot end
NetworkManager networkmanager = new NetworkManager(EnumProtocolDirection.SERVERBOUND);
//ServerConnection.this.h.add(networkmanager);
pending.add(networkmanager); // Paper
channel.pipeline().addLast("packet_handler", networkmanager);
networkmanager.a(new HandshakeListener(ServerConnection.this.f, networkmanager));
}
}).group((EventLoopGroup) lazyinitvar.c()).localAddress(address).bind().syncUninterruptibly());
} }
} }
public void b() { public void stopServer() throws InterruptedException {
this.d = false; this.started = false;
LOGGER.info("Shutting down event loops");
// CraftBukkit start - handle processQueue while closing channels to prevent deadlock for (ChannelFuture future : this.listeningChannels) { // Nacho - deobfuscate listeningChannels
ArrayList<ChannelFuture> futures = new ArrayList<ChannelFuture>();
for (ChannelFuture channelfuture : this.g) {
futures.add(channelfuture.channel().close());
}
for(;;) {
futures.removeIf(java.util.concurrent.Future::isDone);
f.processTasks();
if(futures.isEmpty()) break;
try { try {
Thread.sleep(50); future.channel().close().sync();
} catch(InterruptedException e) { } finally {
e.printStackTrace(); boss.shutdownGracefully();
worker.shutdownGracefully();
} }
} }
// CraftBukkit end
} }
public void processFastPackets() { public void processFastPackets() {
synchronized (this.h) { synchronized (this.connectedChannels) {
for (NetworkManager networkManager : this.h) { for (NetworkManager networkManager : this.connectedChannels) {
networkManager.processFastPackets(); networkManager.processFastPackets();
} }
} }
} }
public void c() { public void c() {
List list = this.h; synchronized (this.connectedChannels) { // Nacho - deobfuscate connectedChannels
synchronized (this.h) {
// Spigot Start // Spigot Start
addPending(); // KigPaper this.addPending(); // Paper
// This prevents players from 'gaming' the server, and strategically relogging to increase their position in the tick order // This prevents players from 'gaming' the server, and strategically relogging to increase their position in the tick order
if ( org.spigotmc.SpigotConfig.playerShuffle > 0 && MinecraftServer.currentTick % org.spigotmc.SpigotConfig.playerShuffle == 0 ) if ( org.spigotmc.SpigotConfig.playerShuffle > 0 && MinecraftServer.currentTick % org.spigotmc.SpigotConfig.playerShuffle == 0 ) {
{ Collections.shuffle( this.connectedChannels); // Nacho - deobfuscate connectedChannels
Collections.shuffle( this.h );
} }
// Spigot End // Spigot End
Iterator iterator = this.h.iterator(); Iterator<NetworkManager> iterator = this.connectedChannels.iterator(); // Nacho - deobfuscate connectedChannels
while (iterator.hasNext()) { while (iterator.hasNext()) {
final NetworkManager networkmanager = (NetworkManager) iterator.next(); final NetworkManager networkmanager = iterator.next();
if (!networkmanager.h()) { if (!networkmanager.h()) {
if (!networkmanager.g()) { if (!networkmanager.isConnected()) {
// Spigot Start // Spigot Start
// Fix a race condition where a NetworkManager could be unregistered just before connection. // Fix a race condition where a NetworkManager could be unregistered just before connection.
if (networkmanager.preparing) continue; if (networkmanager.preparing) continue;
@ -200,25 +192,17 @@ public class ServerConnection {
networkmanager.l(); networkmanager.l();
} else { } else {
try { try {
networkmanager.a(); networkmanager.tick();
} catch (Exception exception) { } catch (Exception exception) {
if (networkmanager.c()) { if (networkmanager.c()) {
CrashReport crashreport = CrashReport.a(exception, "Ticking memory connection"); CrashReport crashreport = CrashReport.a(exception, "Ticking memory connection");
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Ticking connection"); CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Ticking connection");
crashreportsystemdetails.a("Connection", new Callable() { crashreportsystemdetails.a("Connection", networkmanager::toString);
public String a() throws Exception {
return networkmanager.toString();
}
public Object call() throws Exception {
return this.a();
}
});
throw new ReportedException(crashreport); throw new ReportedException(crashreport);
} }
ServerConnection.e.warn("Failed to handle packet for " + networkmanager.getSocketAddress(), exception); ServerConnection.LOGGER.warn("Failed to handle packet for " + networkmanager.getSocketAddress(), exception);
final ChatComponentText chatcomponenttext = new ChatComponentText("Internal server error"); final ChatComponentText chatcomponenttext = new ChatComponentText("Internal server error");
networkmanager.a(new PacketPlayOutKickDisconnect(chatcomponenttext), (GenericFutureListener) future -> networkmanager.close(chatcomponenttext), new GenericFutureListener[0]); networkmanager.a(new PacketPlayOutKickDisconnect(chatcomponenttext), (GenericFutureListener) future -> networkmanager.close(chatcomponenttext), new GenericFutureListener[0]);
@ -232,6 +216,6 @@ public class ServerConnection {
} }
public MinecraftServer d() { public MinecraftServer d() {
return this.f; return this.server;
} }
} }

View File

@ -1,9 +1,12 @@
package net.minecraft.server; package net.minecraft.server;
import com.elevatemc.spigot.config.eSpigotConfig;
import com.elevatemc.spigot.util.OptimizedWorldTileEntitySet;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import net.jafama.FastMath;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.block.BlockState; import org.bukkit.block.BlockState;
@ -63,7 +66,7 @@ public abstract class World implements IBlockAccess {
// Spigot end // Spigot end
protected final Set<Entity> g = Sets.newHashSet(); // Paper protected final Set<Entity> g = Sets.newHashSet(); // Paper
//public final List<TileEntity> h = Lists.newArrayList(); // PaperSpigot - Remove unused list //public final List<TileEntity> h = Lists.newArrayList(); // PaperSpigot - Remove unused list
public final List<TileEntity> tileEntityList = Lists.newArrayList(); public final OptimizedWorldTileEntitySet tileEntityList = new OptimizedWorldTileEntitySet();
private final List<TileEntity> b = Lists.newArrayList(); private final List<TileEntity> b = Lists.newArrayList();
private final Set<TileEntity> c = Sets.newHashSet(); // Paper private final Set<TileEntity> c = Sets.newHashSet(); // Paper
public Set<TileEntity> getTileEntityListUnload() { public Set<TileEntity> getTileEntityListUnload() {
@ -140,7 +143,12 @@ public abstract class World implements IBlockAccess {
private org.spigotmc.TickLimiter tileLimiter; private org.spigotmc.TickLimiter tileLimiter;
private int tileTickPosition; private int tileTickPosition;
public ExecutorService lightingExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("PaperSpigot - Lighting Thread").build()); // PaperSpigot - Asynchronous lighting updates public ExecutorService lightingExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("PaperSpigot - Lighting Thread").build()); // PaperSpigot - Asynchronous lighting updates
public final Map<Explosion.CacheKey, Float> explosionDensityCache = new HashMap<>(); // PaperSpigot - Optimize explosions // IonSpigot start - Optimise Density Cache
public final it.unimi.dsi.fastutil.ints.Int2FloatMap explosionDensityCache = new it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap(); // IonSpigot - Use faster collection here // PaperSpigot - Optimize explosions
{
explosionDensityCache.defaultReturnValue(-1.0f);
}
// IonSpigot end
public java.util.ArrayDeque<BlockRedstoneTorch.RedstoneUpdateInfo> redstoneUpdateInfos; // Paper - Move from Map in BlockRedstoneTorch to here public java.util.ArrayDeque<BlockRedstoneTorch.RedstoneUpdateInfo> redstoneUpdateInfos; // Paper - Move from Map in BlockRedstoneTorch to here
public static long chunkToKey(int x, int z) public static long chunkToKey(int x, int z)
@ -1419,6 +1427,7 @@ public abstract class World implements IBlockAccess {
} }
// Spigot end // Spigot end
if (!eSpigotConfig.entityCollisions) return arraylist;
if (entity instanceof EntityItem) return arraylist; // PaperSpigot - Optimize item movement 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 && !entity.world.paperSpigotConfig.armorStandEntityLookups) return arraylist; // Paper
if (entity instanceof EntityArmorStand) return arraylist; // TacoSpigot - Optimize armor stand movement if (entity instanceof EntityArmorStand) return arraylist; // TacoSpigot - Optimize armor stand movement
@ -1768,14 +1777,15 @@ public abstract class World implements IBlockAccess {
// Spigot start // Spigot start
int tilesThisCycle = 0; int tilesThisCycle = 0;
for (tileTickPosition = 0; tileTickPosition < tileEntityList.size(); tileTickPosition++) { // PaperSpigot - Disable tick limiters Iterator<TileEntity> tileIterator = this.tileEntityList.tickIterator(this.getTime());
while (tileIterator.hasNext()) { // PaperSpigot - Disable tick limiters
tileTickPosition = (tileTickPosition < tileEntityList.size()) ? tileTickPosition : 0; tileTickPosition = (tileTickPosition < tileEntityList.size()) ? tileTickPosition : 0;
TileEntity tileentity = this.tileEntityList.get(tileTickPosition); TileEntity tileentity = (TileEntity) tileIterator.next();
// Spigot start // Spigot start
if (tileentity == null) { if (tileentity == null) {
getServer().getLogger().severe("Spigot has detected a null entity and has removed it, preventing a crash"); getServer().getLogger().severe("Spigot has detected a null entity and has removed it, preventing a crash");
tilesThisCycle--; tilesThisCycle--;
this.tileEntityList.remove(tileTickPosition--); tileIterator.remove();
continue; continue;
} }
// Spigot end // Spigot end
@ -1785,15 +1795,21 @@ public abstract class World implements IBlockAccess {
if (this.isLoaded(blockposition) && this.N.a(blockposition)) { if (this.isLoaded(blockposition) && this.N.a(blockposition)) {
try { try {
// Nacho start - Fix mob spawners still spawning mobs after being broken
if (this.getTileEntity(tileentity.getPosition()) == null){
tileIterator.remove();
continue;
}
// Nacho end
tileentity.tickTimer.startTiming(); // Spigot tileentity.tickTimer.startTiming(); // Spigot
((IUpdatePlayerListBox) tileentity).c(); ((IUpdatePlayerListBox) tileentity).c();
} catch (Throwable throwable2) { } catch (Throwable throwable2) {
// PaperSpigot start - Prevent tile entity and entity crashes // PaperSpigot start - Prevent tile entity and entity crashes
tileentity.tickTimer.stopTiming(); tileentity.tickTimer.stopTiming();
System.err.println("TileEntity threw exception at " + tileentity.world.getWorld().getName() + ":" + tileentity.position.getX() + "," + tileentity.position.getY() + "," + tileentity.position.getZ()); MinecraftServer.LOGGER.error("TileEntity threw exception at " + tileentity.world.getWorld().getName() + ":" + tileentity.position.getX() + "," + tileentity.position.getY() + "," + tileentity.position.getZ());
throwable2.printStackTrace(); throwable2.printStackTrace();
tilesThisCycle--; tilesThisCycle--;
this.tileEntityList.remove(tileTickPosition--); tileIterator.remove();
continue; continue;
// PaperSpigot end // PaperSpigot end
} }
@ -1807,7 +1823,7 @@ public abstract class World implements IBlockAccess {
if (tileentity.x()) { if (tileentity.x()) {
tilesThisCycle--; tilesThisCycle--;
this.tileEntityList.remove(tileTickPosition--); tileIterator.remove();
//this.h.remove(tileentity); // PaperSpigot - Remove unused list //this.h.remove(tileentity); // PaperSpigot - Remove unused list
if (this.isLoaded(tileentity.getPosition())) { if (this.isLoaded(tileentity.getPosition())) {
this.getChunkAtWorldCoords(tileentity.getPosition()).e(tileentity.getPosition()); this.getChunkAtWorldCoords(tileentity.getPosition()).e(tileentity.getPosition());
@ -2270,8 +2286,8 @@ public abstract class World implements IBlockAccess {
double d0 = 1.0D / ((axisalignedbb.d - axisalignedbb.a) * 2.0D + 1.0D); double d0 = 1.0D / ((axisalignedbb.d - axisalignedbb.a) * 2.0D + 1.0D);
double d1 = 1.0D / ((axisalignedbb.e - axisalignedbb.b) * 2.0D + 1.0D); double d1 = 1.0D / ((axisalignedbb.e - axisalignedbb.b) * 2.0D + 1.0D);
double d2 = 1.0D / ((axisalignedbb.f - axisalignedbb.c) * 2.0D + 1.0D); double d2 = 1.0D / ((axisalignedbb.f - axisalignedbb.c) * 2.0D + 1.0D);
double d3 = (1.0D - Math.floor(1.0D / d0) * d0) / 2.0D; double d3 = (1.0D - ((eSpigotConfig.fastMath ? FastMath.floor(1.0D / d0) : Math.floor(1.0D / d0)) * d0)) / 2.0D;
double d4 = (1.0D - Math.floor(1.0D / d2) * d2) / 2.0D; double d4 = (1.0D - ((eSpigotConfig.fastMath ? FastMath.floor(1.0D / d2) : Math.floor(1.0D / d2)) * d2)) / 2.0D;
if (d0 >= 0.0D && d1 >= 0.0D && d2 >= 0.0D) { if (d0 >= 0.0D && d1 >= 0.0D && d2 >= 0.0D) {
int i = 0; int i = 0;
@ -2947,6 +2963,29 @@ public abstract class World implements IBlockAccess {
return null; return null;
} }
// IonSpigot start - Optimise Entity Collisions
public List<Entity> getEntitiesByAmount(Entity entity, AxisAlignedBB axisalignedbb, Predicate<? super Entity> by, int amount) {
List<Entity> entities = new ArrayList<>();
int i = MathHelper.floor((axisalignedbb.a - 2.0D) / 16.0D);
int j = MathHelper.floor((axisalignedbb.d + 2.0D) / 16.0D);
int k = MathHelper.floor((axisalignedbb.c - 2.0D) / 16.0D);
int l = MathHelper.floor((axisalignedbb.f + 2.0D) / 16.0D);
for (int i1 = i; i1 <= j; ++i1) {
for (int j1 = k; j1 <= l; ++j1) {
if (this.isChunkLoaded(i1, j1, true)) {
if (this.getChunkAt(i1, j1).collectEntitiesByAmount(entity, axisalignedbb, entities, by, amount)) {
return entities;
}
}
}
}
return entities;
}
// IonSpigot end
public List<Entity> getEntities(Entity entity, AxisAlignedBB axisalignedbb) { public List<Entity> getEntities(Entity entity, AxisAlignedBB axisalignedbb) {
return this.a(entity, axisalignedbb, IEntitySelector.d); return this.a(entity, axisalignedbb, IEntitySelector.d);
} }

View File

@ -41,6 +41,13 @@ public class WorldNBTStorage implements IDataManager, IPlayerFileData {
} }
this.h(); this.h();
// manually check lock on startup
try {
checkSession0();
} catch (Throwable t) {
org.spigotmc.SneakyThrow.sneaky(t);
}
} }
private void h() { private void h() {
@ -61,7 +68,9 @@ public class WorldNBTStorage implements IDataManager, IPlayerFileData {
return this.baseDir; return this.baseDir;
} }
public void checkSession() throws ExceptionWorldConflict { public void checkSession() throws ExceptionWorldConflict {} // CraftBukkit - throws ExceptionWorldConflict
private void checkSession0() throws ExceptionWorldConflict { // we can safely do so as the server will stop upon detecting a session conflict on startup
try { try {
File file = new File(this.baseDir, "session.lock"); File file = new File(this.baseDir, "session.lock");

View File

@ -347,32 +347,34 @@ public class WorldServer extends World implements IAsyncTaskHandler {
} }
public boolean everyoneDeeplySleeping() { public boolean everyoneDeeplySleeping() {
if (this.O && !this.isClientSide) { // PaperBin start - WorldServer#everyoneDeeplySleeping optimization
Iterator iterator = this.players.iterator(); if (this.players.isEmpty() || this.isClientSide || !this.O) return false;
for (EntityHuman player : this.players) {
if (!player.isSpectator() && !player.isDeeplySleeping() && !player.fauxSleeping) {
return false;
}
}
return true;
// PaperBin end
/*if (this.O && !this.isClientSide) {
Iterator<EntityHuman> iterator = this.players.iterator();
// CraftBukkit - This allows us to assume that some people are in bed but not really, allowing time to pass in spite of AFKers // CraftBukkit - This allows us to assume that some people are in bed but not really, allowing time to pass in spite of AFKers
boolean foundActualSleepers = false; boolean foundActualSleepers = false;
EntityHuman entityhuman; EntityHuman entityhuman;
do { do {
if (!iterator.hasNext()) { if (!iterator.hasNext()) {
return foundActualSleepers; return foundActualSleepers;
} }
entityhuman = iterator.next();
entityhuman = (EntityHuman) iterator.next();
// CraftBukkit start // CraftBukkit start
if (entityhuman.isDeeplySleeping()) { if (entityhuman.isDeeplySleeping()) {
foundActualSleepers = true; foundActualSleepers = true;
} }
} while (!entityhuman.isSpectator() || entityhuman.isDeeplySleeping() || entityhuman.fauxSleeping); } while (!entityhuman.isSpectator() || entityhuman.isDeeplySleeping() || entityhuman.fauxSleeping);
// CraftBukkit end // CraftBukkit end
return false;
} else {
return false;
} }
return false;*/
} }
protected void h() { protected void h() {
@ -422,6 +424,7 @@ public class WorldServer extends World implements IAsyncTaskHandler {
this.a(k, l, chunk); this.a(k, l, chunk);
this.methodProfiler.c("tickChunk"); this.methodProfiler.c("tickChunk");
if (!chunk.areNeighborsLoaded(1)) continue; // Spigot
chunk.b(false); chunk.b(false);
this.methodProfiler.c("thunder"); this.methodProfiler.c("thunder");
int i1; int i1;
@ -658,7 +661,7 @@ public class WorldServer extends World implements IAsyncTaskHandler {
break; break;
} }
if (next.a().isPowerSource() || next.a() instanceof IContainer) { if (next.a().isPowerSource() || next.a() instanceof IContainer || next.a() instanceof BlockFalling) { // IonSpigot - falling blocks should bypass tick cap
iterator.remove(); iterator.remove();
this.V.add(next); this.V.add(next);
} }

View File

@ -793,7 +793,7 @@ public final class CraftServer implements Server {
} }
BlockPosition chunkcoordinates = internal.getSpawn(); BlockPosition chunkcoordinates = internal.getSpawn();
internal.chunkProviderServer.getChunkAt(chunkcoordinates.getX() + j >> 4, chunkcoordinates.getZ() + k >> 4); internal.chunkProviderServer.getChunkAt(chunkcoordinates.getX() + j >> 4, chunkcoordinates.getZ() + k >> 4, () -> {}); // IonSpigot - Async Spawn Chunks
} }
} }
} }
@ -833,6 +833,9 @@ public final class CraftServer implements Server {
return false; return false;
} }
worlds.remove(world.getName().toLowerCase());
console.worlds.remove(console.worlds.indexOf(handle));
if (save) { if (save) {
try { try {
handle.save(true, null); handle.save(true, null);
@ -856,9 +859,6 @@ public final class CraftServer implements Server {
// KigPaper end // KigPaper end
} }
worlds.remove(world.getName().toLowerCase());
console.worlds.remove(console.worlds.indexOf(handle));
// KigPaper start - fix memory leak // KigPaper start - fix memory leak
CraftingManager craftingManager = CraftingManager.getInstance(); CraftingManager craftingManager = CraftingManager.getInstance();
CraftInventoryView lastView = (CraftInventoryView) craftingManager.lastCraftView; CraftInventoryView lastView = (CraftInventoryView) craftingManager.lastCraftView;

View File

@ -1,5 +1,6 @@
package org.bukkit.craftbukkit; package org.bukkit.craftbukkit;
import com.elevatemc.spigot.config.eSpigotConfig;
import com.elevatemc.spigot.util.Constants; import com.elevatemc.spigot.util.Constants;
import com.elevatemc.spigot.util.Dictionary; import com.elevatemc.spigot.util.Dictionary;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
@ -14,6 +15,7 @@ import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import net.jafama.FastMath;
import net.minecraft.server.*; import net.minecraft.server.*;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
@ -415,23 +417,44 @@ public class CraftWorld implements World {
double prevY = loc.getY(); double prevY = loc.getY();
double prevZ = loc.getZ(); double prevZ = loc.getZ();
loc.add(xs, ys, zs); loc.add(xs, ys, zs);
if (loc.getX() < Math.floor(prevX)) { if (eSpigotConfig.fastMath) {
loc.setX(Math.floor(prevX)); if (loc.getX() < FastMath.floor(prevX)) {
} loc.setX(FastMath.floor(prevX));
if (loc.getX() >= Math.ceil(prevX)) { }
loc.setX(Math.ceil(prevX - 0.01)); if (loc.getX() >= FastMath.ceil(prevX)) {
} loc.setX(FastMath.ceil(prevX - 0.01));
if (loc.getY() < Math.floor(prevY)) { }
loc.setY(Math.floor(prevY)); if (loc.getY() < FastMath.floor(prevY)) {
} loc.setY(FastMath.floor(prevY));
if (loc.getY() >= Math.ceil(prevY)) { }
loc.setY(Math.ceil(prevY - 0.01)); if (loc.getY() >= FastMath.ceil(prevY)) {
} loc.setY(FastMath.ceil(prevY - 0.01));
if (loc.getZ() < Math.floor(prevZ)) { }
loc.setZ(Math.floor(prevZ)); if (loc.getZ() < FastMath.floor(prevZ)) {
} loc.setZ(FastMath.floor(prevZ));
if (loc.getZ() >= Math.ceil(prevZ)) { }
loc.setZ(Math.ceil(prevZ - 0.01)); if (loc.getZ() >= Math.ceil(prevZ)) {
loc.setZ(FastMath.ceil(prevZ - 0.01));
}
} else {
if (loc.getX() < Math.floor(prevX)) {
loc.setX(Math.floor(prevX));
}
if (loc.getX() >= Math.ceil(prevX)) {
loc.setX(Math.ceil(prevX - 0.01));
}
if (loc.getY() < Math.floor(prevY)) {
loc.setY(Math.floor(prevY));
}
if (loc.getY() >= Math.ceil(prevY)) {
loc.setY(Math.ceil(prevY - 0.01));
}
if (loc.getZ() < Math.floor(prevZ)) {
loc.setZ(Math.floor(prevZ));
}
if (loc.getZ() >= Math.ceil(prevZ)) {
loc.setZ(Math.ceil(prevZ - 0.01));
}
} }
} }

View File

@ -15,7 +15,7 @@ public class ServerShutdownThread extends Thread {
public void run() { public void run() {
try { try {
server.stop(); server.stop();
} catch (ExceptionWorldConflict ex) { } catch (ExceptionWorldConflict | InterruptedException ex) {
ex.printStackTrace(); ex.printStackTrace();
} finally { } finally {
try { try {

View File

@ -401,7 +401,7 @@ public class PaperSpigotWorldConfig
} }
public static boolean isRedstoneFireBPE; public static boolean isRedstoneFireBPE;
private void isRedstoneFireBPE() { private static void isRedstoneFireBPE() {
isRedstoneFireBPE = getBoolean("redstone-fire-BlockPhysicsEvent", true); isRedstoneFireBPE = getBoolean("redstone-fire-BlockPhysicsEvent", true);
} }
@ -440,10 +440,24 @@ public class PaperSpigotWorldConfig
} }
public static boolean fixEastWest; public static boolean fixEastWest;
private void fixEastWest() { private static void fixEastWest() {
fixEastWest = getBoolean("fix-east-west-cannoning", false); fixEastWest = getBoolean("fix-east-west-cannoning", false);
} }
public static boolean disableFallingBlockStackingAt256; public static boolean disableFallingBlockStackingAt256;
private void DisableFallingBlockStackingAt256() { disableFallingBlockStackingAt256 = getBoolean("disable-falling-block-stacking-at-256", false);} private static void DisableFallingBlockStackingAt256() {
disableFallingBlockStackingAt256 = getBoolean("disable-falling-block-stacking-at-256", false);
}
public static boolean constantExplosions;
public static boolean reducedDensityRays;
private static void explosions() {
constantExplosions = getBoolean("explosions.constant-radius", false);
reducedDensityRays = getBoolean("explosions.reduced-density-rays", false);
}
public static boolean fixSandUnloading;
private static void fixSandUnloading() {
fixSandUnloading = getBoolean("sand.fix-unloading", false);
}
} }

View File

@ -1,631 +0,0 @@
/*
* Copyright 2011-2013 Tyler Blair. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and contributors and should not be interpreted as representing official policies,
* either expressed or implied, of anybody else.
*/
package org.spigotmc;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.configuration.InvalidConfigurationException;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import net.minecraft.server.MinecraftServer;
/**
* <p> The metrics class obtains data about a plugin and submits statistics about it to the metrics backend. </p> <p>
* Public methods provided by this class: </p>
* <code>
* Graph createGraph(String name); <br/>
* void addCustomData(BukkitMetrics.Plotter plotter); <br/>
* void start(); <br/>
* </code>
*/
public class Metrics {
/**
* The current revision number
*/
private final static int REVISION = 6;
/**
* The base url of the metrics domain
*/
private static final String BASE_URL = "http://mcstats.org";
/**
* The url used to report a server's status
*/
private static final String REPORT_URL = "/report/%s";
/**
* The separator to use for custom data. This MUST NOT change unless you are hosting your own version of metrics and
* want to change it.
*/
private static final String CUSTOM_DATA_SEPARATOR = "~~";
/**
* Interval of time to ping (in minutes)
*/
private static final int PING_INTERVAL = 10;
/**
* All of the custom graphs to submit to metrics
*/
private final Set<Graph> graphs = Collections.synchronizedSet(new HashSet<>());
/**
* The default graph, used for addCustomData when you don't want a specific graph
*/
private final Graph defaultGraph = new Graph("Default");
/**
* The plugin configuration file
*/
private final YamlConfiguration configuration;
/**
* The plugin configuration file
*/
private final File configurationFile;
/**
* Unique server id
*/
private final String guid;
/**
* Debug mode
*/
private final boolean debug;
/**
* Lock for synchronization
*/
private final Object optOutLock = new Object();
/**
* The scheduled task
*/
private volatile Timer task = null;
public Metrics() throws IOException {
// load the config
configurationFile = getConfigFile();
configuration = YamlConfiguration.loadConfiguration(configurationFile);
// add some defaults
configuration.addDefault("opt-out", false);
configuration.addDefault("guid", UUID.randomUUID().toString());
configuration.addDefault("debug", false);
// Do we need to create the file?
if (configuration.get("guid", null) == null) {
configuration.options().header("http://mcstats.org").copyDefaults(true);
configuration.save(configurationFile);
}
// Load the guid then
guid = configuration.getString("guid");
debug = configuration.getBoolean("debug", false);
}
/**
* Construct and create a Graph that can be used to separate specific plotters to their own graphs on the metrics
* website. Plotters can be added to the graph object returned.
*
* @param name The name of the graph
* @return Graph object created. Will never return NULL under normal circumstances unless bad parameters are given
*/
public Graph createGraph(final String name) {
if (name == null) {
throw new IllegalArgumentException("Graph name cannot be null");
}
// Construct the graph object
final Graph graph = new Graph(name);
// Now we can add our graph
graphs.add(graph);
// and return back
return graph;
}
/**
* Add a Graph object to BukkitMetrics that represents data for the plugin that should be sent to the backend
*
* @param graph The name of the graph
*/
public void addGraph(final Graph graph) {
if (graph == null) {
throw new IllegalArgumentException("Graph cannot be null");
}
graphs.add(graph);
}
/**
* Adds a custom data plotter to the default graph
*
* @param plotter The plotter to use to plot custom data
*/
public void addCustomData(final Plotter plotter) {
if (plotter == null) {
throw new IllegalArgumentException("Plotter cannot be null");
}
// Add the plotter to the graph o/
defaultGraph.addPlotter(plotter);
// Ensure the default graph is included in the submitted graphs
graphs.add(defaultGraph);
}
/**
* Start measuring statistics. This will immediately create an async repeating task as the plugin and send the
* initial data to the metrics backend, and then after that it will post in increments of PING_INTERVAL * 1200
* ticks.
*
* @return True if statistics measuring is running, otherwise false.
*/
public boolean start() {
synchronized (optOutLock) {
// Did we opt out?
if (isOptOut()) {
return false;
}
// Is metrics already running?
if (task != null) {
return true;
}
// Begin hitting the server with glorious data
task = new Timer("Spigot Metrics Thread", true);
task.scheduleAtFixedRate(new TimerTask() {
private boolean firstPost = true;
public void run() {
try {
// This has to be synchronized or it can collide with the disable method.
synchronized (optOutLock) {
// Disable Task, if it is running and the server owner decided to opt-out
if (isOptOut() && task != null) {
task.cancel();
task = null;
// Tell all plotters to stop gathering information.
for (Graph graph : graphs) {
graph.onOptOut();
}
}
}
// We use the inverse of firstPost because if it is the first time we are posting,
// it is not a interval ping, so it evaluates to FALSE
// Each time thereafter it will evaluate to TRUE, i.e PING!
postPlugin(!firstPost);
// After the first post we set firstPost to false
// Each post thereafter will be a ping
firstPost = false;
} catch (IOException e) {
if (debug) {
Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage());
}
}
}
}, 0, TimeUnit.MINUTES.toMillis(PING_INTERVAL));
return true;
}
}
/**
* Has the server owner denied plugin metrics?
*
* @return true if metrics should be opted out of it
*/
public boolean isOptOut() {
synchronized (optOutLock) {
try {
// Reload the metrics file
configuration.load(getConfigFile());
} catch (IOException | InvalidConfigurationException ex) {
if (debug) {
Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage());
}
return true;
}
return configuration.getBoolean("opt-out", false);
}
}
/**
* Enables metrics for the server by setting "opt-out" to false in the config file and starting the metrics task.
*
* @throws java.io.IOException
*/
public void enable() throws IOException {
// This has to be synchronized or it can collide with the check in the task.
synchronized (optOutLock) {
// Check if the server owner has already set opt-out, if not, set it.
if (isOptOut()) {
configuration.set("opt-out", false);
configuration.save(configurationFile);
}
// Enable Task, if it is not running
if (task == null) {
start();
}
}
}
/**
* Disables metrics for the server by setting "opt-out" to true in the config file and canceling the metrics task.
*
* @throws java.io.IOException
*/
public void disable() throws IOException {
// This has to be synchronized or it can collide with the check in the task.
synchronized (optOutLock) {
// Check if the server owner has already set opt-out, if not, set it.
if (!isOptOut()) {
configuration.set("opt-out", true);
configuration.save(configurationFile);
}
// Disable Task, if it is running
if (task != null) {
task.cancel();
task = null;
}
}
}
/**
* Gets the File object of the config file that should be used to store data such as the GUID and opt-out status
*
* @return the File object for the config file
*/
public File getConfigFile() {
// I believe the easiest way to get the base folder (e.g craftbukkit set via -P) for plugins to use
// is to abuse the plugin object we already have
// plugin.getDataFolder() => base/plugins/PluginA/
// pluginsFolder => base/plugins/
// The base is not necessarily relative to the startup directory.
// File pluginsFolder = plugin.getDataFolder().getParentFile();
// return => base/plugins/PluginMetrics/config.yml
return new File(new File((File) MinecraftServer.getServer().options.valueOf("plugins"), "PluginMetrics"), "config.yml");
}
/**
* Generic method that posts a plugin to the metrics website
*/
private void postPlugin(final boolean isPing) throws IOException {
// Server software specific section
String pluginName = "eSpigot"; // PaperSpigot - We need some usage data // TacoSpigot - its *my* usage data // eSpigot
boolean onlineMode = Bukkit.getServer().getOnlineMode(); // TRUE if online mode is enabled
String pluginVersion = (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown";
String serverVersion = Bukkit.getVersion();
int playersOnline = Bukkit.getServer().getOnlinePlayers().size();
// END server software specific section -- all code below does not use any code outside of this class / Java
// Construct the post data
final StringBuilder data = new StringBuilder();
// The plugin's description file containg all of the plugin data such as name, version, author, etc
data.append(encode("guid")).append('=').append(encode(guid));
encodeDataPair(data, "version", pluginVersion);
encodeDataPair(data, "server", serverVersion);
encodeDataPair(data, "players", Integer.toString(playersOnline));
encodeDataPair(data, "revision", String.valueOf(REVISION));
// New data as of R6
String osname = System.getProperty("os.name");
String osarch = System.getProperty("os.arch");
String osversion = System.getProperty("os.version");
String java_version = System.getProperty("java.version");
int coreCount = Runtime.getRuntime().availableProcessors();
// normalize os arch .. amd64 -> x86_64
if (osarch.equals("amd64")) {
osarch = "x86_64";
}
encodeDataPair(data, "osname", osname);
encodeDataPair(data, "osarch", osarch);
encodeDataPair(data, "osversion", osversion);
encodeDataPair(data, "cores", Integer.toString(coreCount));
encodeDataPair(data, "online-mode", Boolean.toString(onlineMode));
encodeDataPair(data, "java_version", java_version);
// If we're pinging, append it
if (isPing) {
encodeDataPair(data, "ping", "true");
}
// Acquire a lock on the graphs, which lets us make the assumption we also lock everything
// inside of the graph (e.g plotters)
synchronized (graphs) {
for (Graph graph : graphs) {
for (Plotter plotter : graph.getPlotters()) {
// The key name to send to the metrics server
// The format is C-GRAPHNAME-PLOTTERNAME where separator - is defined at the top
// Legacy (R4) submitters use the format Custom%s, or CustomPLOTTERNAME
final String key = String.format("C%s%s%s%s", CUSTOM_DATA_SEPARATOR, graph.getName(), CUSTOM_DATA_SEPARATOR, plotter.getColumnName());
// The value to send, which for the foreseeable future is just the string
// value of plotter.getValue()
final String value = Integer.toString(plotter.getValue());
// Add it to the http post data :)
encodeDataPair(data, key, value);
}
}
}
// Create the url
URL url = new URL(BASE_URL + String.format(REPORT_URL, encode(pluginName)));
// Connect to the website
URLConnection connection;
// Mineshafter creates a socks proxy, so we can safely bypass it
// It does not reroute POST requests so we need to go around it
if (isMineshafterPresent()) {
connection = url.openConnection(Proxy.NO_PROXY);
} else {
connection = url.openConnection();
}
connection.setDoOutput(true);
// Write the data
final OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
writer.write(data.toString());
writer.flush();
// Now read the response
final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
final String response = reader.readLine();
// close resources
writer.close();
reader.close();
if (response == null || response.startsWith("ERR")) {
throw new IOException(response); //Throw the exception
} else {
// Is this the first update this hour?
if (response.contains("OK This is your first update this hour")) {
synchronized (graphs) {
for (Graph graph : graphs) {
for (Plotter plotter : graph.getPlotters()) {
plotter.reset();
}
}
}
}
}
}
/**
* Check if mineshafter is present. If it is, we need to bypass it to send POST requests
*
* @return true if mineshafter is installed on the server
*/
private boolean isMineshafterPresent() {
try {
Class.forName("mineshafter.MineServer");
return true;
} catch (Exception e) {
return false;
}
}
/**
* <p>Encode a key/value data pair to be used in a HTTP post request. This INCLUDES a & so the first key/value pair
* MUST be included manually, e.g:</p>
* <code>
* StringBuffer data = new StringBuffer();
* data.append(encode("guid")).append('=').append(encode(guid));
* encodeDataPair(data, "version", description.getVersion());
* </code>
*
* @param buffer the stringbuilder to append the data pair onto
* @param key the key value
* @param value the value
*/
private static void encodeDataPair(final StringBuilder buffer, final String key, final String value) throws UnsupportedEncodingException {
buffer.append('&').append(encode(key)).append('=').append(encode(value));
}
/**
* Encode text as UTF-8
*
* @param text the text to encode
* @return the encoded text, as UTF-8
*/
private static String encode(final String text) throws UnsupportedEncodingException {
return URLEncoder.encode(text, "UTF-8");
}
/**
* Represents a custom graph on the website
*/
public static class Graph {
/**
* The graph's name, alphanumeric and spaces only :) If it does not comply to the above when submitted, it is
* rejected
*/
private final String name;
/**
* The set of plotters that are contained within this graph
*/
private final Set<Plotter> plotters = new LinkedHashSet<>();
private Graph(final String name) {
this.name = name;
}
/**
* Gets the graph's name
*
* @return the Graph's name
*/
public String getName() {
return name;
}
/**
* Add a plotter to the graph, which will be used to plot entries
*
* @param plotter the plotter to add to the graph
*/
public void addPlotter(final Plotter plotter) {
plotters.add(plotter);
}
/**
* Remove a plotter from the graph
*
* @param plotter the plotter to remove from the graph
*/
public void removePlotter(final Plotter plotter) {
plotters.remove(plotter);
}
/**
* Gets an <b>unmodifiable</b> set of the plotter objects in the graph
*
* @return an unmodifiable {@link java.util.Set} of the plotter objects
*/
public Set<Plotter> getPlotters() {
return Collections.unmodifiableSet(plotters);
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(final Object object) {
if (!(object instanceof Graph)) {
return false;
}
final Graph graph = (Graph) object;
return graph.name.equals(name);
}
/**
* Called when the server owner decides to opt-out of BukkitMetrics while the server is running.
*/
protected void onOptOut() {
}
}
/**
* Interface used to collect custom data for a plugin
*/
public static abstract class Plotter {
/**
* The plot's name
*/
private final String name;
/**
* Construct a plotter with the default plot name
*/
public Plotter() {
this("Default");
}
/**
* Construct a plotter with a specific plot name
*
* @param name the name of the plotter to use, which will show up on the website
*/
public Plotter(final String name) {
this.name = name;
}
/**
* Get the current value for the plotted point. Since this function defers to an external function it may or may
* not return immediately thus cannot be guaranteed to be thread friendly or safe. This function can be called
* from any thread so care should be taken when accessing resources that need to be synchronized.
*
* @return the current value for the point to be plotted.
*/
public abstract int getValue();
/**
* Get the column name for the plotted point
*
* @return the plotted point's column name
*/
public String getColumnName() {
return name;
}
/**
* Called after the website graphs have been updated
*/
public void reset() {
}
@Override
public int hashCode() {
return getColumnName().hashCode();
}
@Override
public boolean equals(final Object object) {
if (!(object instanceof Plotter)) {
return false;
}
final Plotter plotter = (Plotter) object;
return plotter.name.equals(name) && plotter.getValue() == getValue();
}
}
}

View File

@ -58,7 +58,7 @@ public class RestartCommand extends Command
{ {
} }
// Close the socket so we can rebind with the new process // Close the socket so we can rebind with the new process
MinecraftServer.getServer().getServerConnection().b(); MinecraftServer.getServer().getServerConnection().stopServer();
// Give time for it to kick in // Give time for it to kick in
try try