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 );
}
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.TicksPerSecondCommand;
import com.elevatemc.spigot.config.eSpigotConfig;
import com.elevatemc.spigot.handler.MovementHandler;
import com.elevatemc.spigot.handler.PacketHandler;
import com.elevatemc.spigot.util.YamlConfig;
@ -34,6 +35,7 @@ public class eSpigot {
public eSpigot(CraftServer server) {
// Set instance of the server
instance = this;
knockbackConfig = new YamlConfig("knockback.yml");
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
package com.elevatemc.spigot.util;
package com.elevatemc.spigot.exception;
public class CryptException extends Exception {
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;
import com.elevatemc.spigot.config.eSpigotConfig;
import com.elevatemc.spigot.pathsearch.PathSearchThrottlerThread;
import com.elevatemc.spigot.pathsearch.jobs.PathSearchJob;
import net.minecraft.server.NBTCompressedStreamTools;
@ -35,6 +36,8 @@ public class ThreadingManager {
private TaskQueueWorker nbtFiles;
private TaskQueueWorker headConversions;
public static ThreadPoolExecutor asyncExplosionsExecutor;
public ThreadingManager() {
instance = this;
this.pathSearchThrottler = new PathSearchThrottlerThread(2);
@ -44,12 +47,18 @@ public class ThreadingManager {
this.cachedThreadPool = Executors.newCachedThreadPool(this.cachedThreadPoolFactory);
this.nbtFiles = this.createTaskQueueWorker();
this.headConversions = this.createTaskQueueWorker();
if (eSpigotConfig.fixedPoolForTNT)
asyncExplosionsExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(eSpigotConfig.fixedPoolSizeForTNT);
else
asyncExplosionsExecutor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
}
public void shutdown() {
this.pathSearchThrottler.shutdown();
this.timerService.shutdown();
this.cachedThreadPool.shutdown();
this.asyncExplosionsExecutor.shutdown();
while((this.nbtFiles.isActive()) && !this.cachedThreadPool.isTerminated()) {
try {
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;
import com.elevatemc.spigot.config.eSpigotConfig;
import com.elevatemc.spigot.event.BlockDropItemsEvent;
import com.elevatemc.spigot.redstone.PandaRedstoneWire;
import org.bukkit.Bukkit;
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(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(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(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"));

View File

@ -34,7 +34,7 @@ public class BlockFalling extends Block {
if (canFall(world, blockposition.down()) && blockposition.getY() >= 0) {
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) {
// 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);

View File

@ -28,6 +28,7 @@ public class BlockFlowing extends BlockFluids {
org.bukkit.Server server = world.getServer();
org.bukkit.block.Block source = bworld == null ? null : bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
// CraftBukkit end
BlockPosition pos = blockposition.down(); // IonSpigot - Cache Below Position
int i = iblockdata.get(BlockFlowing.LEVEL);
byte b0 = 1;
@ -35,7 +36,7 @@ public class BlockFlowing extends BlockFluids {
b0 = 2;
}
int j = this.getFlowSpeed(world, blockposition); // PaperSpigot
// int j = this.getFlowSpeed(world, blockposition); // PaperSpigot
int k;
if (i > 0) {
@ -43,20 +44,30 @@ public class BlockFlowing extends BlockFluids {
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)) {
enumdirection = (EnumDirection) iterator.next();
for (Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); iterator.hasNext(); l = this.a(world, blockposition.shift(enumdirection), l)) {
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 (i1 >= 8 || l < 0) {
i1 = -1;
}
if (this.e(world, blockposition.up()) >= 0) {
k = this.e(world, blockposition.up());
if (temp >= 0) {
k = temp;
if (k >= 8) {
i1 = k;
} else {
@ -65,7 +76,7 @@ public class BlockFlowing extends BlockFluids {
}
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()) {
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
j *= 4;
}
// IonSpigot start - Unused logic
/*
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) {
this.f(world, blockposition, iblockdata);
@ -85,6 +100,7 @@ public class BlockFlowing extends BlockFluids {
if (i1 < 0 || canFastDrain(world, blockposition)) { // PaperSpigot - Fast draining
world.setAir(blockposition);
} else {
int j = this.getFlowSpeed(world, blockposition); // PaperSpigot // IonSpigot - From above
iblockdata = iblockdata.set(BlockFlowing.LEVEL, i1);
world.setTypeAndData(blockposition, iblockdata, 2);
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
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
if (!canFlowTo(world, source, BlockFace.DOWN)) { return; } // Paper
BlockFromToEvent event = new BlockFromToEvent(source, BlockFace.DOWN);
@ -113,20 +130,21 @@ public class BlockFlowing extends BlockFluids {
server.getPluginManager().callEvent(event);
}
if (!event.isCancelled()) {
if (this.material == Material.LAVA && world.getType(blockposition.down()).getBlock().getMaterial() == Material.WATER) {
world.setTypeUpdate(blockposition.down(), Blocks.STONE.getBlockData());
this.fizz(world, blockposition.down());
if (this.material == Material.LAVA && world.getType(pos).getBlock().getMaterial() == Material.WATER) {
world.setTypeUpdate(pos, Blocks.STONE.getBlockData());
this.fizz(world, pos);
return;
}
if (i >= 8) {
this.flow(world, blockposition.down(), iblockdata2, i);
this.flow(world, pos, iblockdata2, i);
} else {
this.flow(world, blockposition.down(), iblockdata2, i + 8);
this.flow(world, pos, iblockdata2, i + 8);
}
}
// 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);
k = i + b0;
@ -150,7 +168,7 @@ public class BlockFlowing extends BlockFluids {
}
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
@ -188,7 +206,7 @@ public class BlockFlowing extends BlockFluids {
BlockPosition blockposition1 = blockposition.shift(enumdirection1);
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)) {
return i;
}
@ -215,11 +233,11 @@ public class BlockFlowing extends BlockFluids {
BlockPosition blockposition1 = blockposition.shift(enumdirection);
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;
if (this.g(world, blockposition1.down(), world.getType(blockposition1.down()))) {
j = this.a(world, blockposition1, 1, enumdirection.opposite());
if (this.g(world, blockposition1.down(), iblockdata)) { // IonSpigot - Unused check
j = this.a(world, blockposition1, 4, enumdirection.opposite()); // IonSpigot - Optimise Fluids
} else {
j = 0;
}
@ -240,7 +258,11 @@ public class BlockFlowing extends BlockFluids {
private boolean g(World world, BlockPosition blockposition, IBlockData iblockdata) {
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());
}
@ -265,7 +287,7 @@ public class BlockFlowing extends BlockFluids {
private boolean h(World world, BlockPosition blockposition, IBlockData iblockdata) {
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) {
@ -296,7 +318,16 @@ public class BlockFlowing extends BlockFluids {
* PaperSpigot - Data check method for fast draining
*/
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;
}
@ -311,13 +342,15 @@ public class BlockFlowing extends BlockFluids {
result = true;
if (getData(world, position.down()) < 0) {
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;
} 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;
} 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;
} 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;
}
}
@ -326,13 +359,15 @@ public class BlockFlowing extends BlockFluids {
result = true;
if (getData(world, position.down()) < 0 || world.getType(position.up()).getBlock().getMaterial() != Material.AIR) {
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;
} 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;
} 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;
} 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;
}
}

View File

@ -30,7 +30,12 @@ public abstract class BlockFluids extends Block {
}
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) {
@ -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);
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, Constants.EMPTY_ARRAY);
}
// IonSpigot start - Remove unused code
/*
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) {

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) {
// 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);
super.a(world, blockposition, iblockdata, axisalignedbb, list, entity);
float f = 0.125F;
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);
// Reset
this.updateShape(world, blockposition);
}
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;
import com.elevatemc.spigot.util.ObjectMapList;
import com.google.common.base.Predicate;
import com.google.common.collect.Maps;
import com.google.common.collect.Queues;
@ -163,7 +164,7 @@ public class Chunk {
this.heightMap = new int[256];
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);
@ -1034,6 +1035,58 @@ public class Chunk {
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) {
int i = MathHelper.floor((axisalignedbb.b - 2.0D) / 16.0D);
int j = MathHelper.floor((axisalignedbb.e + 2.0D) / 16.0D);
@ -1087,7 +1140,7 @@ public class Chunk {
// PaperSpigot start
int[] counts;
if (ItemStack.class.isAssignableFrom(oclass)) {
if (EntityItem.class.isAssignableFrom(oclass)) {
counts = itemCounts;
} else if (IInventory.class.isAssignableFrom(oclass)) {
counts = inventoryEntityCounts;

View File

@ -23,7 +23,7 @@ import org.bukkit.event.world.ChunkUnloadEvent;
public class ChunkProviderServer implements IChunkProvider {
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 IChunkProvider chunkProvider;
public IChunkLoader chunkLoader; // KigPaper - private -> public
@ -69,8 +69,9 @@ public class ChunkProviderServer implements IChunkProvider {
}
public void queueUnload(int i, int j) {
long key = LongHash.toLong(i, j); // IonSpigot - Only create key once
// 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)) {
return;
}
@ -89,9 +90,9 @@ public class ChunkProviderServer implements IChunkProvider {
if (this.world.worldProvider.e()) {
if (!this.world.c(i, j)) {
// 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) {
c.mustSave = true;
}
@ -99,9 +100,9 @@ public class ChunkProviderServer implements IChunkProvider {
}
} else {
// 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) {
c.mustSave = true;
}
@ -128,7 +129,8 @@ public class ChunkProviderServer implements IChunkProvider {
}
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;
if (this.chunkLoader instanceof ChunkRegionLoader) {
@ -147,7 +149,7 @@ public class ChunkProviderServer implements IChunkProvider {
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 (runnable != null) {
runnable.run();
@ -156,7 +158,8 @@ public class ChunkProviderServer implements IChunkProvider {
return chunk;
}
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;
// CraftBukkit end
@ -173,8 +176,8 @@ public class ChunkProviderServer implements IChunkProvider {
CrashReport crashreport = CrashReport.a(throwable, "Exception generating new chunk");
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Chunk to be generated");
crashreportsystemdetails.a("Location", String.format("%d,%d", new Object[] {i, j}));
crashreportsystemdetails.a("Position hash", LongHash.toLong(i, j)); // CraftBukkit - Use LongHash
crashreportsystemdetails.a("Location", String.format("%d,%d", i, j));
crashreportsystemdetails.a("Position hash", key); // CraftBukkit - Use LongHash
crashreportsystemdetails.a("Generator", this.chunkProvider.getName());
throw new ReportedException(crashreport);
}
@ -182,7 +185,7 @@ public class ChunkProviderServer implements IChunkProvider {
newChunk = true; // CraftBukkit
}
this.chunks.put(LongHash.toLong(i, j), chunk);
this.chunks.put(key, chunk);
chunk.addEntities();
@ -216,16 +219,31 @@ public class ChunkProviderServer implements IChunkProvider {
world.timings.syncChunkLoadTimer.stopTiming(); // Spigot
}
this.unloadQueue.remove(LongHash.toLong(i, j)); // SportPaper
this.unloadQueue.remove(key); // SportPaper
return chunk;
}
// IonSpigot start - Optimise Chunk Getting
private Chunk cachedChunk = null;
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
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 (i != chunk.locX || j != chunk.locZ) {
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();
ex.fillInStackTrace();
ex.printStackTrace();
}
} */
// IonSpigot end
return chunk;
// CraftBukkit end

View File

@ -73,7 +73,10 @@ public class ChunkSection {
}
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() {

View File

@ -196,7 +196,7 @@ public class DedicatedServer extends MinecraftServer implements IMinecraftServer
if (!org.spigotmc.SpigotConfig.lateBind) {
try {
this.aq().bind(bindAddress); // PandaSpigot - Unix domain socket support
this.aq().a(bindAddress, this.R()); // PandaSpigot - Unix domain socket support
} catch (IOException ioexception) {
DedicatedServer.LOGGER.warn("**** FAILED TO BIND TO PORT!");
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) {
try {
this.aq().bind(bindAddress); // PandaSpigot - Unix domain socket support
this.aq().a(bindAddress, this.R()); // PandaSpigot - Unix domain socket support
} catch (IOException ioexception) {
DedicatedServer.LOGGER.warn("**** FAILED TO BIND TO PORT!");
DedicatedServer.LOGGER.warn("The exception was: {}", new Object[] { ioexception.toString()});
@ -434,7 +434,15 @@ public class DedicatedServer extends MinecraftServer implements IMinecraftServer
}
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() {

View File

@ -134,8 +134,13 @@ public abstract class Entity implements ICommandListener {
public boolean valid; // CraftBukkit
public org.bukkit.projectiles.ProjectileSource projectileSource; // CraftBukkit - For projectiles only
public boolean forceExplosionKnockback; // CraftBukkit - SPIGOT-949
public boolean isCannoningEntity; // IonSpigot
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
// IonSpigot start - Lag Compensated Ticking
protected boolean compensated;
protected void tick() {}
// IonSpigot end
public static Random SHARED_RANDOM = new FastRandom() {
private boolean locked = false;
@ -206,6 +211,7 @@ public abstract class Entity implements ICommandListener {
this.defaultActivationState = false;
}
// Spigot end
this.isCannoningEntity = this instanceof EntityTNTPrimed || this instanceof EntityFallingBlock; // IonSpigot
this.datawatcher = new DataWatcher(this);
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
*/
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 cz = (int) locZ >> 4; cz <= (int) (locZ + motZ) >> 4; ++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) {
// 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
// FlamePaper start - Disable Unloaded Chunk Movement
if (!world.chunkProvider.isChunkLoaded((int) locX >> 4, (int) locZ >> 4)) {
@ -486,10 +513,6 @@ public abstract class Entity implements ICommandListener {
this.appendEntityCrashDetails(crashreportsystemdetails);
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
this.world.methodProfiler.a("move");
double d3 = this.locX;
@ -1318,6 +1341,7 @@ public abstract class Entity implements ICommandListener {
public void b(Entity entity, int i) {}
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();
if (!this.dead && s != null) {

View File

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

View File

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

View File

@ -1376,7 +1376,7 @@ public abstract class EntityHuman extends EntityLiving {
}
public void b(Statistic statistic) {
this.a(statistic, 1);
this.a(statistic, 20);
}
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
}
// Spigot end
private int tick;
public void G() {
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) {
ItemStack itemstack = this.h[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));
if (itemstack != null) {
this.c.a(itemstack.B());
@ -1709,15 +1711,10 @@ public abstract class EntityLiving extends Entity {
if (!eSpigotConfig.entityCollisions)
return;
List list = this.world.a(this, this.getBoundingBox().grow(0.20000000298023224D, 0.0D, 0.20000000298023224D), Predicates.and(IEntitySelector.d, new Predicate() {
public boolean a(Entity entity) {
return entity.ae();
}
public boolean apply(Object object) {
return this.a((Entity) object);
}
}));
// IonSpigot start - Optimise Entity Collisions
List list = this.world.getEntitiesByAmount(this, this.getBoundingBox().grow(0.20000000298023224D, 0.0D, 0.20000000298023224D),
input -> IEntitySelector.d.apply(input) && input != null && input.ae(), world.spigotConfig.maxCollisionsPerEntity);
// IonSpigot end
if (this.ad() && !list.isEmpty()) { // Spigot: Add this.ad() condition
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 derailedY = 0.5;
private double derailedZ = 0.5;
private double flyingX = 0.95;
private double flyingY = 0.95;
private double flyingZ = 0.95;
private double flyingX = 0.949999988079071D; // Paper - restore vanilla precision
private double flyingY = 0.949999988079071D; // Paper - restore vanilla precision
private double flyingZ = 0.949999988079071D; // Paper - restore vanilla precision
public double maxSpeed = 0.4D;
// CraftBukkit end

View File

@ -51,6 +51,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
public boolean g;
public int ping;
public boolean viewingCredits;
public List<Entity> lagCompensatedTicking = new ArrayList<>(); // IonSpigot - Lag Compensated Ticking
// CraftBukkit start
public String displayName;
@ -66,7 +67,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
// Spigot start
public boolean collidesWithEntities = true;
public int viewDistance; // PaperSpigot - Player view distance API
private int containerUpdateDelay; // PaperSpigot
/* private int containerUpdateDelay; */ // PaperSpigot
public boolean positiveXMovement, positiveYMovement, positiveZMovement;
public KnockbackProfile knockbackProfile = eSpigot.getInstance().getKnockbackHandler().getActiveProfile();
@ -211,9 +212,9 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
// PaperSpigot start - Configurable container update tick rate
if (--containerUpdateDelay <= 0) {
if (/*--containerUpdateDelay <= 0*/ true) {
this.activeContainer.b();
containerUpdateDelay = world.paperSpigotConfig.containerUpdateTickRate;
/*containerUpdateDelay = world.paperSpigotConfig.containerUpdateTickRate;*/
}
// PaperSpigot end
if (!this.world.isClientSide && !this.activeContainer.a(this)) {
@ -325,6 +326,21 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
public void l() {
try {
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) {
ItemStack itemstack = this.inventory.getItem(i);

View File

@ -6,12 +6,14 @@ import java.util.List;
// CraftBukkit start
import java.util.HashMap;
import com.elevatemc.spigot.config.eSpigotConfig;
import org.bukkit.craftbukkit.entity.CraftLivingEntity;
import org.bukkit.entity.LivingEntity;
// CraftBukkit end
public class EntityPotion extends EntityProjectile {
public boolean compensated = false; // IonSpigot - Lag Compensated Potions
public ItemStack item;
public EntityPotion(World world) {
@ -25,6 +27,12 @@ public class EntityPotion extends EntityProjectile {
public EntityPotion(World world, EntityLiving entityliving, ItemStack itemstack) {
super(world, entityliving);
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) {
@ -66,6 +74,18 @@ public class EntityPotion extends EntityProjectile {
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) {
if (!this.world.isClientSide) {
List list = Items.POTION.h(this.item);

View File

@ -52,7 +52,7 @@ public class EntityTNTPrimed extends Entity {
}
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.lastY = this.locY;
this.lastZ = this.locZ;
@ -181,6 +181,8 @@ public class EntityTNTPrimed extends Entity {
public boolean W() {
if (!world.paperSpigotConfig.fixCannons) return super.W();
// IonSpigot start - Optimise TNT Ticking
/*
// Preserve velocity while calling the super method
double oldMotX = this.motX;
double oldMotY = this.motY;
@ -208,6 +210,7 @@ public class EntityTNTPrimed extends Entity {
}
}
}
*/
return this.inWater;
}

View File

@ -1,5 +1,7 @@
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.Sets;
@ -107,7 +109,7 @@ public class EntityTracker {
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
@ -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) {
//org.spigotmc.AsyncCatcher.catchOp( "entity untrack"); // Spigot
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);
}
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);
}
@ -471,7 +471,7 @@ public class EntityTrackerEntry {
}
}
private Packet c() {
protected Packet c() {
if (this.tracker.dead) {
// CraftBukkit start - Remove useless error spam, just return
// EntityTrackerEntry.p.warn("Fetching addPacket for removed entity");

View File

@ -174,6 +174,10 @@ public enum EnumProtocol {
return this.i;
}
public int getStateId() { // OBFHELPER
return a();
}
public static EnumProtocol a(int i) {
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());
}
public static EnumProtocol getProtocolForPacket(Packet packet) { // OBFHELPER
return a(packet);
}
EnumProtocol(int i, Object object) {
this(i);
}

View File

@ -1,26 +1,27 @@
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.FastRandom;
import com.google.common.collect.Lists;
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
import org.bukkit.craftbukkit.event.CraftEventFactory;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.Location;
import org.bukkit.craftbukkit.event.CraftEventFactory;
import org.bukkit.event.block.BlockExplodeEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
// CraftBukkit end
import java.util.*;
import java.util.concurrent.CompletableFuture;
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 b;
private final Random c = CACHED_RANDOM;
@ -51,118 +52,110 @@ public class Explosion {
return;
}
// CraftBukkit end
HashSet hashset = Sets.newHashSet();
boolean flag = true;
// HashSet<BlockPosition> hashset = Sets.newHashSet();
int i;
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
for (int k = 0; k < 16; ++k) {
for (i = 0; i < 16; ++i) {
for (j = 0; j < 16; ++j) {
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 = 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;
}
}
}
if (!this.world.paperSpigotConfig.optimizeLiquidExplosions || !b.getMaterial().isLiquid()) { // TacoSpigot - skip calculating what blocks to blow up in water/lava
it.unimi.dsi.fastutil.longs.LongSet set = new it.unimi.dsi.fastutil.longs.LongOpenHashSet();
searchForBlocks(set, chunk);
for (it.unimi.dsi.fastutil.longs.LongIterator iterator = set.iterator(); iterator.hasNext(); ) {
this.blocks.add(BlockPosition.fromLong(iterator.nextLong()));
}
}
}
this.blocks.addAll(hashset);
// this.blocks.addAll(hashset);
float f3 = this.size * 2.0F;
i = MathHelper.floor(this.posX - (double) f3 - 1.0D);
j = MathHelper.floor(this.posX + (double) f3 + 1.0D);
int l = MathHelper.floor(this.posY - (double) f3 - 1.0D);
int i1 = MathHelper.floor(this.posY + (double) f3 + 1.0D);
int j1 = MathHelper.floor(this.posZ - (double) f3 - 1.0D);
int k1 = MathHelper.floor(this.posZ + (double) f3 + 1.0D);
// IonSpigot start - Faster Entity Iteration
i = MathHelper.floor(this.posX - (double) f3 - 1.0D) >> 4;
j = MathHelper.floor(this.posX + (double) f3 + 1.0D) >> 4;
int l = MathHelper.clamp(MathHelper.floor(this.posY - (double) f3 - 1.0D) >> 4, 0, 15);
int i1 = MathHelper.clamp(MathHelper.floor(this.posY + (double) f3 + 1.0D) >> 4, 0, 15);
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
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
Vec3D vec3d = new Vec3D(this.posX, this.posY, this.posZ);
for (Object o : list) {
Entity entity = (Entity) o;
for (int chunkX = i; chunkX <= j; ++chunkX) {
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()) {
double d7 = entity.f(this.posX, this.posY, this.posZ) / (double) f3;
if (d7 <= 1.0D) {
if (!entity.dead) {
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 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;
d9 /= 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
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) {
continue;
}
// CraftBukkit end
double d14 = entity instanceof EntityHuman && world.paperSpigotConfig.disableExplosionKnockback ? 0 : EnchantmentProtection.a(entity, d13); // PaperSpigot
// Paper - Optimize explosions
// double d12 = this.getBlockDensity(vec3d, entity);
double finalD = d8;
double finalD1 = d9;
double finalD11 = d10;
this.getBlockDensity(vec3d, entity.getBoundingBox()).thenAccept((d12) -> MinecraftServer.getServer().addMainThreadTask(() -> {
double d13 = (1.0D - d7) * d12;
// PaperSpigot start - Fix cannons
/*
entity.motX += d8 * d14;
entity.motY += d9 * d14;
entity.motZ += d10 * d14;
*/
// 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.isCannoningEntity) {
entity.g(finalD * d13, finalD1 * d13, finalD11 * d13);
return;
}
// IonSpigot end
if (entity instanceof EntityHuman && !((EntityHuman) entity).abilities.isInvulnerable && !world.paperSpigotConfig.disableExplosionKnockback) { // PaperSpigot
this.k.put((EntityHuman) entity, new Vec3D(d8 * d13, d9 * d13, d10 * d13));
}
// entity.damageEntity(DamageSource.explosion(this), (float) ((int) ((d13 * d13 + d13) / 2.0D * 8.0D * (double) f3 + 1.0D))); // CraftBukkit start
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) {
@ -194,18 +187,20 @@ public class Explosion {
}
}
boolean cancelled;
List<org.bukkit.block.Block> bukkitBlocks;
float yield;
boolean cancelled = false;
List<org.bukkit.block.Block> bukkitBlocks = blockList;
float yield = 0.3F; // default
if (explode != null) {
EntityExplodeEvent event = new EntityExplodeEvent(explode, location, blockList, 0.3F);
this.world.getServer().getPluginManager().callEvent(event);
cancelled = event.isCancelled();
bukkitBlocks = event.blockList();
yield = event.getYield();
if (eSpigotConfig.fireEntityExplodeEvent) {
EntityExplodeEvent event = new EntityExplodeEvent(explode, location, blockList, yield);
this.world.getServer().getPluginManager().callEvent(event);
cancelled = event.isCancelled();
bukkitBlocks = event.blockList();
yield = event.getYield();
}
} else {
BlockExplodeEvent event = new BlockExplodeEvent(location.getBlock(), blockList, 0.3F);
BlockExplodeEvent event = new BlockExplodeEvent(location.getBlock(), blockList, yield);
this.world.getServer().getPluginManager().callEvent(event);
cancelled = event.isCancelled();
bukkitBlocks = event.blockList();
@ -231,6 +226,8 @@ public class Explosion {
Block block = this.world.getType(blockposition).getBlock();
world.spigotConfig.antiXrayInstance.updateNearbyBlocks(world, blockposition); // Spigot
// IonSpigot start - Optimise Explosions
/*
if (flag) {
double d0 = (float) blockposition.getX() + this.world.random.nextFloat();
double d1 = (float) blockposition.getY() + this.world.random.nextFloat();
@ -249,9 +246,11 @@ public class Explosion {
d3 *= d7;
d4 *= 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.SMOKE_NORMAL, d0, d1, d2, 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);
}
*/
// IonSpigot end
if (block.getMaterial() != Material.AIR) {
if (block.a(this)) {
@ -270,6 +269,7 @@ public class Explosion {
while (iterator.hasNext()) {
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) {
// CraftBukkit start - Ignition by explosion
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;
}
// PaperSpigot start - Optimize explosions
private float getBlockDensity(Vec3D vec3d, AxisAlignedBB aabb) {
if (!this.world.paperSpigotConfig.optimizeExplosions) {
return this.world.a(vec3d, aabb);
}
// IonSpigot start - Block Searching Improvements
private final static List<double[]> VECTORS = Lists.newArrayListWithCapacity(1352);
CacheKey key = new CacheKey(this, aabb);
Float blockDensity = this.world.explosionDensityCache.get(key);
if (blockDensity == null) {
blockDensity = this.world.a(vec3d, aabb);
this.world.explosionDensityCache.put(key, blockDensity);
}
static {
for (int k = 0; k < 16; ++k) {
for (int i = 0; i < 16; ++i) {
for (int j = 0; j < 16; ++j) {
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 {
private final World world;
private final double posX, posY, posZ;
@ -327,12 +489,12 @@ public class Explosion {
this.posX = explosion.posX;
this.posY = explosion.posY;
this.posZ = explosion.posZ;
this.minX = aabb.a;
this.minY = aabb.b;
this.minZ = aabb.c;
this.maxX = aabb.d;
this.maxY = aabb.e;
this.maxZ = aabb.f;
this.minX = aabb.getMinX();
this.minY = aabb.getMinY();
this.minZ = aabb.getMinZ();
this.maxX = aabb.getMaxX();
this.maxY = aabb.getMaxY();
this.maxZ = aabb.getMaxZ();
}
@Override
@ -380,5 +542,7 @@ public class Explosion {
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()) {
this.networkManager.a(new PacketLoginOutSetCompression(this.server.aK()), new ChannelFutureListener() {
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

View File

@ -1,5 +1,8 @@
package net.minecraft.server;
import com.elevatemc.spigot.config.eSpigotConfig;
import net.jafama.FastMath;
import java.util.Random;
import java.util.UUID;
@ -11,30 +14,34 @@ public class MathHelper {
private static final double d;
private static final double[] e;
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) {
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) {
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) {
return (float) Math.sqrt((double) f);
return (float) (fastMathMode ? (FastMath.sqrt(f)) : (Math.sqrt(f)));
}
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) {
if (fastMathMode) return FastMath.floorToInt(f);
int i = (int) f;
return f < (float) i ? i - 1 : i;
}
public static int floor(double d0) {
if (fastMathMode) return FastMath.floorToInt(d0);
int i = (int) d0;
return d0 < (double) i ? i - 1 : i;
@ -55,12 +62,15 @@ public class MathHelper {
}
public static int f(float f) {
if (fastMathMode) return FastMath.ceilToInt(f);
int i = (int) f;
return f > (float) i ? i + 1 : i;
}
public static int f(double d0) {
if (fastMathMode) return FastMath.ceilToInt(d0);
int i = (int) d0;
return d0 > (double) i ? i + 1 : i;
@ -155,7 +165,7 @@ public class MathHelper {
}
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) {
@ -261,11 +271,11 @@ public class MathHelper {
double d10 = d5 + d9;
if (flag2) {
d10 = 1.5707963267948966D - d10;
d10 = (fastMathMode ? (FastMath.PI / 2) : (1.5707963267948966D)) - d10;
}
if (flag1) {
d10 = 3.141592653589793D - d10;
d10 = (fastMathMode ? (FastMath.PI) : (3.141592653589793D)) - d10;
}
if (flag) {
@ -300,7 +310,7 @@ public class MathHelper {
for (i = 0; i < 257; ++i) {
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.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
synchronized (stopLock) {
if (hasStopped) return;
@ -615,7 +615,7 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
}
// CraftBukkit end
if (this.aq() != null) {
this.aq().b();
this.aq().stopServer();
}
if (this.v != null) {
@ -997,7 +997,7 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
if (this.getPlayerList().getPlayerCount() != 0) // Tuinity
{
// 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) {
PlayerConnection connection = player.playerConnection;
if (connection != null) {
@ -1406,6 +1406,8 @@ public abstract class MinecraftServer implements Runnable, ICommandListener, IAs
public abstract boolean ai();
public abstract ServerConnection.EventGroupType getTransport();
public boolean getPVP() {
return this.pvpMode;
}

View File

@ -1,69 +1,61 @@
package net.minecraft.server;
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.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.epoll.EpollEventLoopGroup;
import io.netty.channel.local.LocalChannel;
import io.netty.channel.local.LocalEventLoopGroup;
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.util.AttributeKey;
import io.netty.util.concurrent.AbstractEventExecutor;
import io.netty.util.concurrent.Future;
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.Validate;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import java.net.SocketAddress;
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<?>> {
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");
public static final Marker b = MarkerManager.getMarker("NETWORK_PACKETS", NetworkManager.a);
public static final AttributeKey<EnumProtocol> c = AttributeKey.valueOf("protocol");
public static final LazyInitVar<NioEventLoopGroup> d = new LazyInitVar() {
protected NioEventLoopGroup a() {
return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).build());
}
private final EnumProtocolDirection h;
private final Queue<NetworkManager.QueuedPacket> i = Queues.newConcurrentLinkedQueue();
private final ReentrantReadWriteLock j = new ReentrantReadWriteLock();
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;
// Spigot End
private PacketListener m;
private IChatBaseComponent n;
private boolean encrypted; // Nacho - deobfuscate
private boolean isDisconnectionHandled; // Nacho - deobfuscate
protected Object init() {
return this.a();
}
};
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();
// eSpigot start - faster packets
private final Queue<NetworkManager.QueuedPacket> fastPackets = Queues.newConcurrentLinkedQueue();
public static final Set<Class<? extends Packet<?>>> FAST_ELIGIBLE = new LinkedHashSet<>();
static {
@ -75,31 +67,13 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
FAST_ELIGIBLE.add(PacketPlayOutEntity.PacketPlayOutRelEntityMoveLook.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
volatile boolean canFlush = true;
// Spigot End
private PacketListener m;
private IChatBaseComponent n;
private boolean o;
private boolean p;
private final java.util.concurrent.atomic.AtomicInteger packetWrites = new java.util.concurrent.atomic.AtomicInteger();
private int flushPacketsStart;
public NetworkManager(EnumProtocolDirection enumprotocoldirection) {
this.h = enumprotocoldirection;
}
private final Object flushLock = new Object();
void disableAutomaticFlush() {
synchronized (this.flushLock) {
@ -109,56 +83,50 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
}
void enableAutomaticFlush() {
synchronized (this.flushLock) {
synchronized (this.flushLock)
{
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
}
}
}
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
private void flush() {
if (this.channel.eventLoop().inEventLoop())
this.channel.flush();
public NetworkManager(EnumProtocolDirection enumprotocoldirection) {
this.h = enumprotocoldirection;
}
public void channelActive(ChannelHandlerContext channelhandlercontext) throws Exception {
super.channelActive(channelhandlercontext);
this.channel = channelhandlercontext.channel();
this.l = this.channel.remoteAddress();
// Spigot Start
this.preparing = false;
// Spigot End
try {
this.a(EnumProtocol.HANDSHAKING);
this.setProtocol(EnumProtocol.HANDSHAKING);
} catch (Throwable throwable) {
NetworkManager.g.fatal(throwable);
NetworkManager.LOGGER.fatal(throwable);
}
}
public void setupEncryption(javax.crypto.SecretKey key) throws CryptException {
if (!this.o) {
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);
}
}
public void setProtocol(EnumProtocol protocol) {
a(protocol);
}
// Paper end
public void a(EnumProtocol enumprotocol) {
this.channel.attr(NetworkManager.c).set(enumprotocol);
public void a(EnumProtocol protocol) {
this.channel.attr(NetworkManager.ATTRIBUTE_PROTOCOL).set(protocol);
this.channel.config().setAutoRead(true);
NetworkManager.g.debug("Enabled auto read");
}
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 {
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) {
chatmessage = new ChatMessage("disconnect.timeout");
} else {
@ -175,7 +157,7 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
}
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 {
@ -183,7 +165,6 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
try {
packet.a(this.m);
if (this.m instanceof PlayerConnection) {
try {
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) {
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;
}
//sendPacket
public void handle(Packet packet) {
if (this.g()) {
this.m();
this.a(packet, null);
} 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);
if (this.isConnected()) {
this.sendPacketQueue();
this.dispatchPacket(packet, null, Boolean.TRUE);
} else {
this.j.writeLock().lock();
try {
if (FAST_ELIGIBLE.contains(packet.getClass()))
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)));
this.i.add(new NetworkManager.QueuedPacket(packet));
} finally {
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) {
this.packetWrites.getAndIncrement(); // must be before using canFlush
boolean effectiveFlush = shouldFlush == null ? this.canFlush : shouldFlush;
final boolean flush = effectiveFlush
|| packet instanceof PacketPlayOutKeepAlive
|| packet instanceof PacketPlayOutKickDisconnect
|| FAST_ELIGIBLE.contains(packet.getClass());
final EnumProtocol enumprotocol = EnumProtocol.a(packet);
final EnumProtocol enumprotocol1 = this.channel.attr(NetworkManager.c).get();
//sendPacket
public void a(Packet packet, GenericFutureListener<? extends Future<? super Void>> listener, GenericFutureListener<? extends Future<? super Void>>... listeners) {
if (this.isConnected()) {
this.sendPacketQueue();
this.dispatchPacket(packet, ArrayUtils.insert(0, listeners, listener), Boolean.TRUE);
} else {
this.j.writeLock().lock();
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) {
NetworkManager.g.debug("Disabled auto read");
this.channel.config().setAutoRead(false);
}
if (this.channel.eventLoop().inEventLoop()) {
if (enumprotocol != enumprotocol1) {
this.a(enumprotocol);
this.setProtocol(enumprotocol);
}
ChannelFuture channelfuture = flush ? this.channel.writeAndFlush(packet) : this.channel.write(packet);
if (agenericfuturelistener != null)
channelfuture.addListeners(agenericfuturelistener);
if (listeners != null) {
channelfuture.addListeners(listeners);
}
channelfuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
} else {
Runnable command = new Runnable() { // PandaSpigot - optimize packets that are not flushed
public void run() {
}
else {
// 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) {
NetworkManager.this.a(enumprotocol);
this.setProtocol(enumprotocol);
}
try {
ChannelFuture channelfuture1 = (flush) ? NetworkManager.this.channel.writeAndFlush(packet) : NetworkManager.this.channel.write(packet); // PandaSpigot - add flush parameter
if (agenericfuturelistener != null)
channelfuture1.addListeners(agenericfuturelistener);
ChannelFuture channelfuture1 = (flush) ? this.channel.writeAndFlush(packet) : this.channel.write(packet); // Tuinity - add flush parameter
if (listeners != null) {
channelfuture1.addListeners(listeners);
}
channelfuture1.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
} catch (Exception e) {
g.error("NetworkException: ", e);
close(new ChatMessage("disconnect.genericReason", "Internal Exception: " + e.getMessage()));
LOGGER.error("NetworkException: " + getPlayer(), e);
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 {
// if flushing, just schedule like normal
this.channel.eventLoop().execute(command);
}
// PandaSpigot end
}
}
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;
}
// explicitly declare a variable to make the lambda use the type
choice2 = () -> {
if (enumprotocol != enumprotocol1) {
this.setProtocol(enumprotocol);
}
}
} finally {
this.j.readLock().unlock();
try {
// Nacho - why not remove the check below if the check is done above? just code duplication...
// 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() {
if (this.fastPackets.isEmpty()) // Don't lock
return;
@ -349,10 +318,10 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
Iterator<QueuedPacket> iterator = this.fastPackets.iterator();
while (iterator.hasNext()) {
QueuedPacket queued = iterator.next();
Packet<?> packet = queued.a;
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);
this.dispatchPacket(packet, queued.b, (!iterator.hasNext() && (needsFlush || this.canFlush)) ? Boolean.TRUE : Boolean.FALSE);
hasWrotePacket = true;
}
} finally {
@ -361,9 +330,37 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
}
}
}
// eSpigot end
public void a() {
this.m();
private void sendPacketQueue() {
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) {
((IUpdatePlayerListBox) this.m).c();
}
@ -371,12 +368,16 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
this.channel.flush();
}
public void a() {
this.tick();
}
public SocketAddress getSocketAddress() {
return this.l;
}
public void close(IChatBaseComponent ichatbasecomponent) {
this.i.clear(); // KigPaper
this.i.clear(); // FlamePaper - Minetick fix memory leaks
// Spigot Start
this.preparing = false;
// 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.n = ichatbasecomponent;
}
}
public boolean c() {
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.channel.pipeline().addBefore("splitter", "decrypt", new PacketDecrypter(MinecraftEncryption.a(2, 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() {
return this.channel != null && this.channel.isOpen();
return this.isConnected();
}
public boolean h() {
@ -417,25 +440,17 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
this.channel.config().setAutoRead(false);
}
public void a(int i)
{
// Nacho start - OBFHELPER
this.setupCompression(i);
}
public void setupCompression(int compressionThreshold) {
// Nacho end
if (compressionThreshold >= 0)
{
public void setupCompression(int compressionThreshold) { // Nacho - deobfuscate
if (compressionThreshold >= 0) {
VelocityCompressor compressor = Natives.compress.get().create(-1); // Paper
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 {
this.channel.pipeline().addBefore("decoder", "decompress", new PacketDecompressor(compressor, compressionThreshold)); // Paper
}
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 {
this.channel.pipeline().addBefore("encoder", "compress", new PacketCompressor(compressor, compressionThreshold)); // Paper
}
@ -450,10 +465,13 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
}
}
public void l() {
if (this.channel != null && !this.channel.isOpen()) {
if (!this.p) {
this.p = true;
public void handleDisconnection()
{
if (this.channel != null && !this.channel.isOpen())
{
if (!this.isDisconnectionHandled) // Nacho - deobfuscate isDisconnectionHandled
{
this.isDisconnectionHandled = true; // Nacho - deobfuscate isDisconnectionHandled
if (this.j() != null) {
this.getPacketListener().a(this.j());
} else if (this.getPacketListener() != null) {
@ -461,33 +479,40 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet> {
}
this.i.clear(); // Free up packet queue.
} 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
// this.a(channelhandlercontext, object);
// FlamePaper - Check if channel is opened before reading packet
if (g()) {
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
public SocketAddress getRawAddress() {
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
}

View File

@ -13,33 +13,30 @@ import org.apache.logging.log4j.MarkerManager;
public class PacketDecoder extends ByteToMessageDecoder {
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;
public PacketDecoder(EnumProtocolDirection enumprotocoldirection) {
this.c = enumprotocoldirection;
}
protected void decode(ChannelHandlerContext channelhandlercontext, ByteBuf bytebuf, List<Object> list) throws Exception {
if (bytebuf.readableBytes() != 0) {
PacketDataSerializer packetdataserializer = new PacketDataSerializer(bytebuf);
int i = packetdataserializer.e();
Packet packet = ((EnumProtocol) channelhandlercontext.channel().attr(NetworkManager.c).get()).a(this.c, i);
protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> list) throws Exception {
if (!bytebuf.isReadable()) return;
if (packet == null) {
throw new IOException("Bad packet id " + i);
} else {
packet.a(packetdataserializer);
if (packetdataserializer.readableBytes() > 0) {
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()});
}
PacketDataSerializer packetDataHelper = new PacketDataSerializer(bytebuf);
int packetId = packetDataHelper.e();
Packet<?> packet = ctx.channel().attr(NetworkManager.ATTRIBUTE_PROTOCOL).get().a(this.c, packetId);
if (packet == null)
throw new IOException("Bad packet id " + packetId);
}
}
}
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) {
this.threshold = var1;
}
public void setThreshold(int var1) { // Nacho - deobfuscate
a(var1);
}
}

View File

@ -1,52 +1,44 @@
package net.minecraft.server;
import com.elevatemc.spigot.exception.ExploitException;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
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 Marker b = MarkerManager.getMarker("PACKET_SENT", NetworkManager.b);
// private static final Logger a = LogManager.getLogger();
// private static final Marker b = MarkerManager.getMarker("PACKET_SENT", NetworkManager.PACKET_MARKER);
private final EnumProtocolDirection c;
public PacketEncoder(EnumProtocolDirection enumprotocoldirection) {
this.c = enumprotocoldirection;
}
protected void a(ChannelHandlerContext channelhandlercontext, Packet packet, ByteBuf bytebuf) throws Exception {
Integer integer = ((EnumProtocol) channelhandlercontext.channel().attr(NetworkManager.c).get()).a(this.c, packet);
protected void a(ChannelHandlerContext ctx, Packet<?> packet, ByteBuf bytebuf) throws Exception {
Integer packetId = (ctx.channel().attr(NetworkManager.c).get()).a(this.c, packet);
if (PacketEncoder.a.isDebugEnabled()) {
PacketEncoder.a.debug(PacketEncoder.b, "OUT: [{}:{}] {}", new Object[] { channelhandlercontext.channel().attr(NetworkManager.c).get(), integer, packet.getClass().getName()});
}
/*if (PacketEncoder.a.isDebugEnabled()) {
PacketEncoder.a.debug(PacketEncoder.b, "OUT: [{}:{}] {}", ctx.channel().attr(NetworkManager.ATTRIBUTE_PROTOCOL).get(), packetId, packet.getClass().getName());
}*/
if (integer == null) {
throw new IOException("Can\'t serialize unregistered packet");
if (packetId == null) {
throw new IOException("Can't serialize unregistered packet");
} else {
PacketDataSerializer packetdataserializer = new PacketDataSerializer(bytebuf);
packetdataserializer.b(integer.intValue());
PacketDataSerializer serializer = new PacketDataSerializer(bytebuf);
serializer.b(packetId); // Nacho - deobfuscate writeVarInt
try {
if (packet instanceof PacketPlayOutNamedEntitySpawn) {
packet = packet;
}
packet.b(packetdataserializer);
} catch (Throwable throwable) {
PacketEncoder.a.error(throwable);
packet.b(serializer);
} catch (ExploitException ex) {
MinecraftServer.LOGGER.error("Exploit exception: " + ctx.channel().attr(NetworkManager.ATTRIBUTE_PROTOCOL).get());
}
}
}
protected void encode(ChannelHandlerContext channelhandlercontext, Packet object, ByteBuf bytebuf) throws Exception {
this.a(channelhandlercontext, object, bytebuf);
@Override
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 IpBanList l;
private final OpList operators;
private Set<UUID> fastOperator = new HashSet<>();
private final WhiteList whitelist;
private final Map<UUID, ServerStatisticManager> o;
public IPlayerFileData playerFileData;
@ -76,9 +75,6 @@ public abstract class PlayerList {
this.k = new GameProfileBanList(PlayerList.a);
this.l = new IpBanList(PlayerList.b);
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.o = Maps.newHashMap();
this.server = minecraftserver;
@ -1020,7 +1016,6 @@ public abstract class PlayerList {
public void addOp(GameProfile gameprofile) {
this.operators.add(new OpListEntry(gameprofile, this.server.p(), this.operators.b(gameprofile)));
this.fastOperator.add(gameprofile.getId());
// CraftBukkit start
Player player = server.server.getPlayer(gameprofile.getId());
@ -1032,7 +1027,6 @@ public abstract class PlayerList {
public void removeOp(GameProfile gameprofile) {
this.operators.remove(gameprofile);
this.fastOperator.remove(gameprofile.getId());
// CraftBukkit start
Player player = server.server.getPlayer(gameprofile.getId());
@ -1043,11 +1037,11 @@ public abstract class PlayerList {
}
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) {
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) {

View File

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

View File

@ -1,9 +1,12 @@
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.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import net.jafama.FastMath;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.block.BlockState;
@ -63,7 +66,7 @@ public abstract class World implements IBlockAccess {
// Spigot end
protected final Set<Entity> g = Sets.newHashSet(); // Paper
//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 Set<TileEntity> c = Sets.newHashSet(); // Paper
public Set<TileEntity> getTileEntityListUnload() {
@ -140,7 +143,12 @@ public abstract class World implements IBlockAccess {
private org.spigotmc.TickLimiter tileLimiter;
private int tileTickPosition;
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 static long chunkToKey(int x, int z)
@ -1419,6 +1427,7 @@ public abstract class World implements IBlockAccess {
}
// Spigot end
if (!eSpigotConfig.entityCollisions) return arraylist;
if (entity instanceof EntityItem) return arraylist; // PaperSpigot - Optimize item movement
if (entity instanceof EntityArmorStand && !entity.world.paperSpigotConfig.armorStandEntityLookups) return arraylist; // Paper
if (entity instanceof EntityArmorStand) return arraylist; // TacoSpigot - Optimize armor stand movement
@ -1768,14 +1777,15 @@ public abstract class World implements IBlockAccess {
// Spigot start
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;
TileEntity tileentity = this.tileEntityList.get(tileTickPosition);
TileEntity tileentity = (TileEntity) tileIterator.next();
// Spigot start
if (tileentity == null) {
getServer().getLogger().severe("Spigot has detected a null entity and has removed it, preventing a crash");
tilesThisCycle--;
this.tileEntityList.remove(tileTickPosition--);
tileIterator.remove();
continue;
}
// Spigot end
@ -1785,15 +1795,21 @@ public abstract class World implements IBlockAccess {
if (this.isLoaded(blockposition) && this.N.a(blockposition)) {
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
((IUpdatePlayerListBox) tileentity).c();
} catch (Throwable throwable2) {
// PaperSpigot start - Prevent tile entity and entity crashes
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();
tilesThisCycle--;
this.tileEntityList.remove(tileTickPosition--);
tileIterator.remove();
continue;
// PaperSpigot end
}
@ -1807,7 +1823,7 @@ public abstract class World implements IBlockAccess {
if (tileentity.x()) {
tilesThisCycle--;
this.tileEntityList.remove(tileTickPosition--);
tileIterator.remove();
//this.h.remove(tileentity); // PaperSpigot - Remove unused list
if (this.isLoaded(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 d1 = 1.0D / ((axisalignedbb.e - axisalignedbb.b) * 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 d4 = (1.0D - Math.floor(1.0D / d2) * d2) / 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 - ((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) {
int i = 0;
@ -2947,6 +2963,29 @@ public abstract class World implements IBlockAccess {
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) {
return this.a(entity, axisalignedbb, IEntitySelector.d);
}

View File

@ -41,6 +41,13 @@ public class WorldNBTStorage implements IDataManager, IPlayerFileData {
}
this.h();
// manually check lock on startup
try {
checkSession0();
} catch (Throwable t) {
org.spigotmc.SneakyThrow.sneaky(t);
}
}
private void h() {
@ -61,7 +68,9 @@ public class WorldNBTStorage implements IDataManager, IPlayerFileData {
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 {
File file = new File(this.baseDir, "session.lock");

View File

@ -347,32 +347,34 @@ public class WorldServer extends World implements IAsyncTaskHandler {
}
public boolean everyoneDeeplySleeping() {
if (this.O && !this.isClientSide) {
Iterator iterator = this.players.iterator();
// PaperBin start - WorldServer#everyoneDeeplySleeping optimization
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
boolean foundActualSleepers = false;
EntityHuman entityhuman;
do {
if (!iterator.hasNext()) {
return foundActualSleepers;
}
entityhuman = (EntityHuman) iterator.next();
entityhuman = iterator.next();
// CraftBukkit start
if (entityhuman.isDeeplySleeping()) {
foundActualSleepers = true;
}
} while (!entityhuman.isSpectator() || entityhuman.isDeeplySleeping() || entityhuman.fauxSleeping);
// CraftBukkit end
return false;
} else {
return false;
}
return false;*/
}
protected void h() {
@ -422,6 +424,7 @@ public class WorldServer extends World implements IAsyncTaskHandler {
this.a(k, l, chunk);
this.methodProfiler.c("tickChunk");
if (!chunk.areNeighborsLoaded(1)) continue; // Spigot
chunk.b(false);
this.methodProfiler.c("thunder");
int i1;
@ -658,7 +661,7 @@ public class WorldServer extends World implements IAsyncTaskHandler {
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();
this.V.add(next);
}

View File

@ -793,7 +793,7 @@ public final class CraftServer implements Server {
}
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;
}
worlds.remove(world.getName().toLowerCase());
console.worlds.remove(console.worlds.indexOf(handle));
if (save) {
try {
handle.save(true, null);
@ -856,9 +859,6 @@ public final class CraftServer implements Server {
// KigPaper end
}
worlds.remove(world.getName().toLowerCase());
console.worlds.remove(console.worlds.indexOf(handle));
// KigPaper start - fix memory leak
CraftingManager craftingManager = CraftingManager.getInstance();
CraftInventoryView lastView = (CraftInventoryView) craftingManager.lastCraftView;

View File

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

View File

@ -401,7 +401,7 @@ public class PaperSpigotWorldConfig
}
public static boolean isRedstoneFireBPE;
private void isRedstoneFireBPE() {
private static void isRedstoneFireBPE() {
isRedstoneFireBPE = getBoolean("redstone-fire-BlockPhysicsEvent", true);
}
@ -440,10 +440,24 @@ public class PaperSpigotWorldConfig
}
public static boolean fixEastWest;
private void fixEastWest() {
private static void fixEastWest() {
fixEastWest = getBoolean("fix-east-west-cannoning", false);
}
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
MinecraftServer.getServer().getServerConnection().b();
MinecraftServer.getServer().getServerConnection().stopServer();
// Give time for it to kick in
try