Upstream Paper

This commit is contained in:
Sotr
2019-04-07 02:25:49 +08:00
31 changed files with 350 additions and 147 deletions

View File

@@ -111,8 +111,12 @@ public class PaperCommand extends Command {
break;
case "ver":
case "version":
org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version").execute(sender, commandLabel, new String[0]);
break;
Command ver = org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version");
if (ver != null) {
ver.execute(sender, commandLabel, new String[0]);
break;
}
// else - fall through to default
default:
sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
return false;

View File

@@ -71,8 +71,8 @@ public class PaperConfig {
commands = new HashMap<String, Command>();
commands.put("paper", new PaperCommand("paper"));
version = getInt("config-version", 17);
set("config-version", 17);
version = getInt("config-version", 18);
set("config-version", 18);
readConfig(PaperConfig.class, null);
}

View File

@@ -129,10 +129,19 @@ public class PaperWorldConfig {
if (entityTNTHeightNerf != 0) log("TNT Entity Height Limit set to Y: " + entityTNTHeightNerf);
}
public boolean netherVoidTopDamage;
private void netherVoidTopDamage() {
netherVoidTopDamage = getBoolean( "nether-ceiling-void-damage", false );
log("Top of the nether void damage: " + netherVoidTopDamage);
public int netherVoidTopDamageHeight;
public boolean doNetherTopVoidDamage() { return netherVoidTopDamageHeight > 0; }
private void netherVoidTopDamageHeight() {
netherVoidTopDamageHeight = getInt("nether-ceiling-void-damage-height", 0);
log("Top of the nether void damage height: " + netherVoidTopDamageHeight);
if (PaperConfig.version < 18) {
boolean legacy = getBoolean("nether-ceiling-void-damage", false);
if (legacy) {
netherVoidTopDamageHeight = 128;
set("nether-ceiling-void-damage-height", netherVoidTopDamageHeight);
}
}
}
public boolean queueLightUpdates;
@@ -545,11 +554,6 @@ public class PaperWorldConfig {
log("ArmorStand ticking is " + (this.armorStandTick ? "enabled" : "disabled") + " by default");
}
public boolean optimizeLight = true;
private void optimizeLight() {
this.optimizeLight = getBoolean("optimize-light", optimizeLight);
}
public boolean antiXray;
public boolean asynchronous;
public EngineMode engineMode;

View File

@@ -583,7 +583,7 @@ public class Chunk implements IChunkAccess {
} else {
if (flag1) {
this.initLighting();
} else if (!world.paperConfig.optimizeLight || block != block1) { // Paper - Optimize light recalculations
} else {
this.runOrQueueLightUpdate(() -> { // Paper - Queue light update
int i1 = iblockdata.b(this.world, blockposition);
int j1 = iblockdata1.b(this.world, blockposition);
@@ -732,40 +732,41 @@ public class Chunk implements IChunkAccess {
if (k >= this.entitySlices.length) {
k = this.entitySlices.length - 1;
}
// Paper - remove from any old list if its in one
List<Entity> nextSlice = this.entitySlices[k]; // the next list to be added to
List<Entity> currentSlice = entity.entitySlice;
if (nextSlice == currentSlice) {
if (World.DEBUG_ENTITIES) MinecraftServer.LOGGER.warn("Entity was already in this chunk!" + entity, new Throwable());
return; // ??? silly plugins
}
if (currentSlice != null && currentSlice.contains(entity)) {
// Still in an old chunk...
if (World.DEBUG_ENTITIES) MinecraftServer.LOGGER.warn("Entity is still in another chunk!" + entity, new Throwable());
Chunk chunk = entity.getCurrentChunk();
if (chunk != null) {
chunk.removeEntity(entity);
} else {
removeEntity(entity);
}
currentSlice.remove(entity); // Just incase the above did not remove from the previous slice
}
// Paper end
if (!entity.inChunk || entity.getCurrentChunk() != this) entityCounts.increment(entity.getMinecraftKeyString()); // Paper
entity.inChunk = true;
entity.setCurrentChunk(this); // Paper
entity.chunkX = this.locX;
entity.chunkY = k;
entity.chunkZ = this.locZ;
this.entitySlices[k].add(entity);
// Paper start
List<Entity> entitySlice = this.entitySlices[k];
boolean inThis = entitySlice.contains(entity);
List<Entity> currentSlice = entity.entitySlice;
if (inThis || (currentSlice != null && currentSlice.contains(entity))) {
if (currentSlice == entitySlice || inThis) {
return;
} else {
Chunk chunk = entity.getCurrentChunk();
if (chunk != null) {
chunk.removeEntity(entity);
} else {
removeEntity(entity);
}
currentSlice.remove(entity); // Just incase the above did not remove from this target slice
}
}
entity.entitySlice = entitySlice;
entitySlice.add(entity);
entity.entitySlice = this.entitySlices[k]; // Paper
this.markDirty();
if (entity instanceof EntityItem) {
itemCounts[k]++;
} else if (entity instanceof IInventory) {
inventoryEntityCounts[k]++;
}
entity.setCurrentChunk(this);
entityCounts.increment(entity.getMinecraftKeyString());
// Paper end
}
@@ -773,7 +774,7 @@ public class Chunk implements IChunkAccess {
((HeightMap) this.heightMap.get(heightmap_type)).a(along);
}
public void removeEntity(Entity entity) { b(entity); } // Paper - OBFHELPER
public void removeEntity(Entity entity) { this.b(entity); } // Paper - OBFHELPER
public void b(Entity entity) {
this.a(entity, entity.chunkY);
}
@@ -787,17 +788,19 @@ public class Chunk implements IChunkAccess {
i = this.entitySlices.length - 1;
}
// Paper start
if (entity.entitySlice == null || !entity.entitySlice.contains(entity) || entitySlices[i] == entity.entitySlice) {
if (entity.currentChunk != null && entity.currentChunk.get() == this) entity.setCurrentChunk(null);
if (entitySlices[i] == entity.entitySlice) {
entity.entitySlice = null;
}
if (!this.entitySlices[i].remove(entity)) { return; }
if (!this.entitySlices[i].remove(entity)) {
return;
}
this.markDirty();
if (entity instanceof EntityItem) {
itemCounts[i]--;
} else if (entity instanceof IInventory) {
inventoryEntityCounts[i]--;
}
entity.setCurrentChunk(null);
entityCounts.decrement(entity.getMinecraftKeyString());
// Paper end
}
@@ -1071,8 +1074,6 @@ public class Chunk implements IChunkAccess {
}
}
// Spigot End
entity.setCurrentChunk(null); // Paper
entity.entitySlice = null; // Paper
// Do not pass along players, as doing so can get them stuck outside of time.
// (which for example disables inventory icon updates and prevents block breaking)

View File

@@ -204,10 +204,13 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
}
}
private final Object legacyStructureLock = new Object(); // Paper
public void getPersistentStructureLegacy(DimensionManager dimensionmanager, @Nullable PersistentCollection persistentcollection) { this.a(dimensionmanager, persistentcollection); } // Paper
public void a(DimensionManager dimensionmanager, @Nullable PersistentCollection persistentcollection) {
if (this.e == null) {
synchronized (legacyStructureLock){ if (this.e == null) { // Paper
this.e = PersistentStructureLegacy.a(dimensionmanager, persistentcollection);
}
} } } // Paper
}

View File

@@ -501,7 +501,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
this.aa();
}
*/
this.checkAndDoHeightDamage();
this.performVoidDamage();
// Paper end
if (!this.world.isClientSide) {
@@ -513,9 +513,12 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
}
// Paper start
protected void checkAndDoHeightDamage() {
if (this.locY < -64.0D || (this.world.paperConfig.netherVoidTopDamage && this.world.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER && this.locY >= 128.0D)) {
this.kill();
protected void performVoidDamage() {
if (this.locY < -64.0D || (this.world.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER
&& world.paperConfig.doNetherTopVoidDamage()
&& this.locY >= world.paperConfig.netherVoidTopDamageHeight)) {
this.doVoidDamage();
}
}
// Paper end
@@ -587,7 +590,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
this.fireTicks = 0;
}
protected final void kill() { this.aa(); } // Paper - OBFHELPER
protected final void doVoidDamage() { this.aa(); } // Paper - OBFHELPER
protected void aa() {
this.die();
}
@@ -1890,7 +1893,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
}
// Paper start
private java.lang.ref.WeakReference<Chunk> currentChunk = null;
java.lang.ref.WeakReference<Chunk> currentChunk = null;
public void setCurrentChunk(Chunk chunk) {
this.currentChunk = chunk != null ? new java.lang.ref.WeakReference<>(chunk) : null;

View File

@@ -404,6 +404,7 @@ public class EntityFishingHook extends Entity {
} else {
this.h = MathHelper.nextInt(this.random, world.paperConfig.fishingMinTicks, world.paperConfig.fishingMaxTicks); // Paper
this.h -= this.aA * 20 * 5;
this.h = Math.max(0, this.h); // Paper - Don't allow negative values
}
}

View File

@@ -259,6 +259,7 @@ public abstract class EntityInsentient extends EntityLiving {
public void tick() {
super.tick();
if (isTypeNotPersistent() && hasBeenCounted == this.isPersistent()) ((com.destroystokyo.paper.PaperWorldEntityList) this.world.entityList).updateEntityCount(this, hasBeenCounted ? -1 : 1); // Paper - adjust count if persistence state changes
if (!this.world.isClientSide) {
this.dl();
if (this.ticksLived % 5 == 0) {

View File

@@ -200,7 +200,7 @@ public abstract class EntityMinecartAbstract extends Entity implements INamableT
this.aa();
}
*/
this.checkAndDoHeightDamage();
this.performVoidDamage();
// Paper end
int i;

View File

@@ -490,6 +490,46 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
});
}
// Paper start - process inventory
private static void processKeep(org.bukkit.event.entity.PlayerDeathEvent event, NonNullList<ItemStack> inv) {
List<org.bukkit.inventory.ItemStack> itemsToKeep = event.getItemsToKeep();
if (inv == null) {
// remainder of items left in toKeep - plugin added stuff on death that wasn't in the initial loot?
if (!itemsToKeep.isEmpty()) {
for (org.bukkit.inventory.ItemStack itemStack : itemsToKeep) {
event.getEntity().getInventory().addItem(itemStack);
}
}
return;
}
for (int i = 0; i < inv.size(); ++i) {
ItemStack item = inv.get(i);
if (EnchantmentManager.shouldNotDrop(item) || itemsToKeep.isEmpty() || item.isEmpty()) {
inv.set(i, ItemStack.NULL_ITEM);
continue;
}
final org.bukkit.inventory.ItemStack bukkitStack = item.getBukkitStack();
boolean keep = false;
final Iterator<org.bukkit.inventory.ItemStack> iterator = itemsToKeep.iterator();
while (iterator.hasNext()) {
final org.bukkit.inventory.ItemStack itemStack = iterator.next();
if (bukkitStack.equals(itemStack)) {
iterator.remove();
keep = true;
break;
}
}
if (!keep) {
inv.set(i, ItemStack.NULL_ITEM);
}
}
}
// Paper end
public void die(DamageSource damagesource) {
boolean flag = this.world.getGameRules().getBoolean("showDeathMessages");
// CraftBukkit start - fire PlayerDeathEvent
@@ -567,8 +607,14 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.releaseShoulderEntities();
// we clean the player's inventory after the EntityDeathEvent is called so plugins can get the exact state of the inventory.
if (!event.getKeepInventory()) {
this.inventory.clear();
// Paper start - replace logic
for (NonNullList<ItemStack> inv : this.inventory.getComponents()) {
processKeep(event, inv);
}
processKeep(event, null);
// Paper end
}
this.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.DEATH); // Paper
this.setSpectatorTarget(this); // Remove spectated target
// CraftBukkit end

View File

@@ -98,7 +98,7 @@ public class EntityTrackerEntry {
}
// PAIL : rename
if (this.tracker instanceof EntityItemFrame && this.a % 20 == 0) { // Paper
if (this.tracker instanceof EntityItemFrame /*&& this.a % 10 == 0*/) { // CraftBukkit - Moved below, should always enter this block
EntityItemFrame entityitemframe = (EntityItemFrame) this.tracker;
ItemStack itemstack = entityitemframe.getItem();

View File

@@ -20,11 +20,19 @@ import java.util.function.BiFunction;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import org.omg.CORBA.DefinitionKind;
public class GameRules {
private static final TreeMap<String, GameRules.GameRuleDefinition> a = (TreeMap<String, GameRules.GameRuleDefinition>) SystemUtils.a(new TreeMap<String, GameRules.GameRuleDefinition>(), (treemap) -> { // Akarin - fixes decompile error
// Paper start - Optimize GameRules
private static final int RULES_SIZE = 256;
private static <K, V> java.util.LinkedHashMap<K, V> linkedMapOf(final int capacity, final TreeMap<K, V> map) {
final java.util.LinkedHashMap<K, V> ret = new java.util.LinkedHashMap<>(capacity);
ret.putAll(map);
return ret;
}
private static final java.util.LinkedHashMap<String, GameRuleDefinition> a = GameRules.linkedMapOf(RULES_SIZE, SystemUtils.a(new TreeMap(), (treemap) -> { // Paper - decompile fix
// Paper end
treemap.put("doFireTick", new GameRules.GameRuleDefinition("true", GameRules.EnumGameRuleType.BOOLEAN_VALUE));
treemap.put("mobGriefing", new GameRules.GameRuleDefinition("true", GameRules.EnumGameRuleType.BOOLEAN_VALUE));
treemap.put("keepInventory", new GameRules.GameRuleDefinition("false", GameRules.EnumGameRuleType.BOOLEAN_VALUE));
@@ -58,12 +66,10 @@ public class GameRules {
treemap.put("doLimitedCrafting", new GameRules.GameRuleDefinition("false", GameRules.EnumGameRuleType.BOOLEAN_VALUE));
treemap.put("maxCommandChainLength", new GameRules.GameRuleDefinition("65536", GameRules.EnumGameRuleType.NUMERICAL_VALUE));
treemap.put("announceAdvancements", new GameRules.GameRuleDefinition("true", GameRules.EnumGameRuleType.BOOLEAN_VALUE));
});
private final HashObjObjMap<String, GameRules.GameRuleValue> b = HashObjObjMaps.newImmutableMap(Maps.transformValues(GameRules.a, GameRules.GameRuleDefinition::a)); // Akarin
})); // Paper - Optimize GameRules
private final java.util.LinkedHashMap<String, GameRuleValue> b = new java.util.LinkedHashMap<>(RULES_SIZE); // Paper - Optimize GameRules
public GameRules() {
// Akarin start
/*
Iterator iterator = GameRules.a.entrySet().iterator();
while (iterator.hasNext()) {
@@ -71,8 +77,6 @@ public class GameRules {
this.b.put(entry.getKey(), ((GameRules.GameRuleDefinition) entry.getValue()).a());
}
*/
// Akarin end
}
@@ -127,7 +131,7 @@ public class GameRules {
return (GameRules.GameRuleValue) this.b.get(s);
}
public static TreeMap<String, GameRuleDefinition> getGameRules() {
public static java.util.LinkedHashMap<String, GameRuleDefinition> getGameRules() { // Paper - Optimize GameRules
return GameRules.a;
}
@@ -144,7 +148,7 @@ public class GameRules {
private final Supplier<ArgumentType<?>> d;
private final BiFunction<CommandContext<CommandListenerWrapper>, String, String> e;
private EnumGameRuleType(Supplier supplier, BiFunction<CommandContext<CommandListenerWrapper>, String, String> bifunction) { // Akarin - fixes decompile error
private EnumGameRuleType(Supplier supplier, BiFunction<CommandContext<CommandListenerWrapper>, String, String> bifunction) { // Paper - decompile fix
this.d = supplier;
this.e = bifunction;
}

View File

@@ -6,6 +6,7 @@ import javax.annotation.Nullable;
public interface IChunkLoader {
void getPersistentStructureLegacy(DimensionManager dimensionmanager, @Nullable PersistentCollection persistentcollection); // Paper
void loadEntities(NBTTagCompound nbttagcompound, Chunk chunk); // Paper - Async Chunks
Object[] loadChunk(GeneratorAccess generatoraccess, int i, int j, Consumer<Chunk> consumer) throws IOException; // Paper - Async Chunks
@Nullable

View File

@@ -38,10 +38,19 @@ import org.bukkit.event.world.StructureGrowEvent;
public final class ItemStack {
private static final Logger c = LogManager.getLogger();
public static final ItemStack a = new ItemStack((Item) null);
public static final ItemStack a = new ItemStack((Item) null);public static final ItemStack NULL_ITEM = a; // Paper - OBFHELPER
public static final DecimalFormat b = D();
private int count;
private int e;
// Paper start
private org.bukkit.craftbukkit.inventory.CraftItemStack bukkitStack;
public org.bukkit.inventory.ItemStack getBukkitStack() {
if (bukkitStack == null || bukkitStack.getHandle() != this) {
bukkitStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this);
}
return bukkitStack;
}
// Paper end
@Deprecated
private Item item;
NBTTagCompound tag; // Paper -> package private
@@ -232,15 +241,7 @@ public final class ItemStack {
enuminteractionresult = EnumInteractionResult.FAIL; // cancel placement
// PAIL: Remove this when MC-99075 fixed
placeEvent.getPlayer().updateInventory();
// Paper start
for (Map.Entry<BlockPosition, TileEntity> e : world.capturedTileEntities.entrySet()) {
if (e.getValue() instanceof TileEntityLootable) {
((TileEntityLootable) e.getValue()).setLootTable(null);
}
}
// Paper end
world.capturedTileEntities.clear(); // Paper - clear out tile entities as chests and such will pop loot
// revert back all captured blocks
for (BlockState blockstate : blocks) {
blockstate.update(true, false);
@@ -787,6 +788,7 @@ public final class ItemStack {
// CraftBukkit start
@Deprecated
public void setItem(Item item) {
this.bukkitStack = null; // Paper
this.item = item;
}
// CraftBukkit end

View File

@@ -286,6 +286,36 @@ public class LoginListener implements PacketLoginInListener, ITickable {
}
}
// Paper start - Delay async prelogin until plugins are ready
private static volatile Object blockingLogins = new Object();
public static void checkStartupAndBlock() {
final Object lock = LoginListener.blockingLogins;
if (lock != null) {
synchronized (lock) {
for (;;) {
if (LoginListener.blockingLogins == null) {
return;
}
try {
lock.wait();
} catch (final InterruptedException ignore) {// handled by the if statement above
Thread.currentThread().interrupt();
}
}
}
}
}
public static void allowLogins() {
final Object lock = LoginListener.blockingLogins;
synchronized (lock) {
LoginListener.blockingLogins = null;
lock.notifyAll();
}
}
// Paper end
// Spigot start
public class LoginHandler {
@@ -296,6 +326,7 @@ public class LoginListener implements PacketLoginInListener, ITickable {
return;
}
// Paper end
LoginListener.checkStartupAndBlock(); // Paper - Delay async login events until plugins are ready
String playerName = i.getName();
java.net.InetAddress address = ((java.net.InetSocketAddress) networkManager.getSocketAddress()).getAddress();
java.util.UUID uniqueId = i.getId();

View File

@@ -609,6 +609,7 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati
this.x = 0;
// CraftBukkit Start
this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.POSTWORLD);
LoginListener.allowLogins(); // Paper - Allow logins once postworld
this.server.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.STARTUP));
// CraftBukkit end
}
@@ -932,6 +933,7 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati
co.aikar.timings.TimingsManager.FULL_SERVER_TICK.startTiming(); // Paper // Akarin
this.slackActivityAccountant.tickStarted(); // Spigot
long i = SystemUtils.getMonotonicNanos(); long startTime = i; // Paper
new com.destroystokyo.paper.event.server.ServerTickStartEvent(this.ticks+1).callEvent(); // Paper
++this.ticks;
if (this.S) {
@@ -1000,7 +1002,12 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati
PaperLightingQueue.processQueue(startTime); // Paper
expiringMaps.removeIf(ExpiringMap::clean); // Paper
this.slackActivityAccountant.tickEnded(l); // Spigot
co.aikar.timings.TimingsManager.FULL_SERVER_TICK.stopTiming(); // Paper // Akarin
// Paper start
long endTime = System.nanoTime();
long remaining = (TICK_TIME - (endTime - lastTick)) - catchupTime;
new com.destroystokyo.paper.event.server.ServerTickEndEvent(this.ticks, ((double)(endTime - lastTick) / 1000000D), remaining).callEvent();
// Paper end
co.aikar.timings.TimingsManager.FULL_SERVER_TICK.stopTiming(); // Paper
}
public void b(BooleanSupplier booleansupplier) {

View File

@@ -20,7 +20,7 @@ public class PlayerInventory implements IInventory {
public final NonNullList<ItemStack> items;
public final NonNullList<ItemStack> armor;
public final NonNullList<ItemStack> extraSlots;
private final List<NonNullList<ItemStack>> f;
private final List<NonNullList<ItemStack>> f;List<NonNullList<ItemStack>> getComponents() { return f; } // Paper - OBFHELPER
public int itemInHandIndex;
public EntityHuman player;
private ItemStack carried;

View File

@@ -29,7 +29,7 @@ public class RegionFile {
private RandomAccessFile c;private RandomAccessFile getDataFile() { return c; } // Paper - OBFHELPER
private final int[] d = new int[1024];private int[] offsets = d; // Paper - OBFHELPER
private final int[] e = new int[1024];private int[] timestamps = e; // Paper - OBFHELPER
private List<Boolean> f;
private List<Boolean> f; private List<Boolean> getFreeSectors() { return this.f; } // Paper - OBFHELPER
private int g;
private long h;
@@ -211,32 +211,31 @@ public class RegionFile {
protected synchronized void a(int i, int j, byte[] abyte, int k) {
try {
int l = this.getOffset(i, j);
int i1 = l >> 8;
int j1 = l & 255;
int i1 = l >> 8; final int oldSectorOffset = i1; // Paper - store variable for later
int j1 = l & 255; final int oldSectorCount; // Paper - store variable for later
// Spigot start
if (j1 == 255) {
this.c.seek(i1 * 4096);
j1 = (this.c.readInt() + 4) / 4096 + 1;
}
// Spigot end
oldSectorCount = j1; // Paper - store variable for later (watch out for re-assignments of j1)
int k1 = (k + 5) / 4096 + 1;
if (k1 >= 256) {
// Spigot start
if (!USE_SPIGOT_OVERSIZED_METHOD) throw new ChunkTooLargeException(i, j, k1); // Paper - throw error instead
if (!USE_SPIGOT_OVERSIZED_METHOD && !RegionFileCache.isOverzealous()) throw new ChunkTooLargeException(i, j, k1); // Paper - throw error instead
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING,"Large Chunk Detected: ({0}, {1}) Size: {2} {3}", new Object[]{i, j, k1, this.b});
if (!ENABLE_EXTENDED_SAVE) return;
// Spigot end
}
if (i1 != 0 && j1 == k1) {
if (false && i1 != 0 && j1 == k1) { // Paper - We never want to overrite old data
this.a(i1, abyte, k);
} else {
int l1;
for (l1 = 0; l1 < j1; ++l1) {
this.f.set(i1 + l1, true);
}
// Paper - We do not free old sectors until we are done writing the new chunk data
l1 = this.f.indexOf(true);
int i2 = 0;
@@ -263,13 +262,13 @@ public class RegionFile {
if (i2 >= k1) {
i1 = l1;
this.a(i, j, l1 << 8 | (k1 > 255 ? 255 : k1)); // Spigot
//this.a(i, j, l1 << 8 | (k1 > 255 ? 255 : k1)); // Spigot // Paper - We only write to header after we've written chunk data
for (j2 = 0; j2 < k1; ++j2) {
this.f.set(i1 + j2, false);
}
this.a(i1, abyte, k);
this.writeChunk(i, j,i1 << 8 | (k1 > 255 ? 255 : k1), i1, abyte, k); // Paper - Ensure we do not corrupt region files
} else {
this.c.seek(this.c.length());
i1 = this.f.size();
@@ -280,9 +279,14 @@ public class RegionFile {
}
this.g += 4096 * k1;
this.a(i1, abyte, k);
this.a(i, j, i1 << 8 | (k1 > 255 ? 255 : k1)); // Spigot
this.writeChunk(i, j, i1 << 8 | (k1 > 255 ? 255 : k1), i1, abyte, k); // Paper - Ensure we do not corrupt region files
}
// Paper start - Now that we've written the new chunk we can free the old data
for (int off = 0; off < oldSectorCount; ++off) {
this.getFreeSectors().set(oldSectorOffset + off, true);
}
// Paper end
}
this.b(i, j, (int) (SystemUtils.getTimeMillis() / 1000L));
@@ -292,10 +296,10 @@ public class RegionFile {
}
private void writeChunkData(final int sectorOffset, final byte[] data, final int dataLength) throws IOException { this.a(sectorOffset, data, dataLength); } // Paper - OBFHELPER
private void a(int i, byte[] abyte, int j) throws IOException {
this.c.seek((long) (i * 4096));
this.c.writeInt(j + 1);
this.c.writeByte(2);
this.writeIntAndByte(j + 1, (byte)2); // Paper - Avoid 4 io write calls
this.c.write(abyte, 0, j);
}
@@ -311,16 +315,17 @@ public class RegionFile {
return this.getOffset(i, j) != 0;
}
private void updateChunkHeader(final int x, final int z, final int offset) throws IOException { this.a(x, z, offset); } // Paper - OBFHELPER
private void a(int i, int j, int k) throws IOException {
this.d[i + j * 32] = k;
this.c.seek((long) ((i + j * 32) * 4));
this.c.writeInt(k);
this.writeInt(k); // Paper - Avoid 3 io write calls
}
private void b(int i, int j, int k) throws IOException {
this.e[i + j * 32] = k;
this.c.seek((long) (4096 + (i + j * 32) * 4));
this.c.writeInt(k);
this.writeInt(k); // Paper - Avoid 3 io write calls
}
public void close() throws IOException {
@@ -331,6 +336,40 @@ public class RegionFile {
}
// Paper start
private static final boolean FLUSH_ON_SAVE = Boolean.getBoolean("paper.flush-on-save");
private void syncRegionFile() throws IOException {
if (!FLUSH_ON_SAVE) {
return;
}
this.getDataFile().getFD().sync(); // rethrow exception as we want to avoid corrupting a regionfile
}
private final java.nio.ByteBuffer scratchBuffer = java.nio.ByteBuffer.allocate(8);
private void writeInt(final int value) throws IOException {
synchronized (scratchBuffer) {
this.scratchBuffer.putInt(0, value);
this.getDataFile().write(this.scratchBuffer.array(), 0, 4);
}
}
// writes v1 then v2
private void writeIntAndByte(final int v1, final byte v2) throws IOException {
synchronized (scratchBuffer) {
this.scratchBuffer.putInt(0, v1);
this.scratchBuffer.put(4, v2);
this.getDataFile().write(this.scratchBuffer.array(), 0, 5);
}
}
private void writeChunk(final int x, final int z, final int chunkHeaderData,
final int chunkOffset, final byte[] chunkData, final int chunkDataLength) throws IOException {
this.writeChunkData(chunkOffset, chunkData, chunkDataLength);
this.syncRegionFile(); // Sync is required to ensure the previous data is written successfully
this.updateChunkHeader(x, z, chunkHeaderData);
this.syncRegionFile(); // Ensure header changes go through
}
public synchronized void deleteChunk(int j1) {
backup();
int k = offsets[j1];
@@ -501,31 +540,26 @@ public class RegionFile {
int length = out.size();
RegionFile.this.a(this.b, this.c, bytes, length); // Paper - change to bytes/length
// Paper end
}
}
private static final byte[] compressionBuffer = new byte[1024 * 64]; // 64k fits most standard chunks input size even, ideally 1 pass through zlib
private static final java.util.zip.Deflater deflater = new java.util.zip.Deflater();
// since file IO is single threaded, no benefit to using per-region file buffers/synchronization, we can change that later if it becomes viable.
private static DirectByteArrayOutputStream compressData(byte[] buf, int length) throws IOException {
final java.util.zip.Deflater deflater;
if (length > 1024 * 512) {
deflater = new java.util.zip.Deflater(9);
} else if (length > 1024 * 128) {
deflater = new java.util.zip.Deflater(8);
} else {
deflater = new java.util.zip.Deflater(6);
synchronized (deflater) {
deflater.setInput(buf, 0, length);
deflater.finish();
DirectByteArrayOutputStream out = new DirectByteArrayOutputStream(length);
while (!deflater.finished()) {
out.write(compressionBuffer, 0, deflater.deflate(compressionBuffer));
}
out.close();
deflater.reset();
return out;
}
deflater.setInput(buf, 0, length);
deflater.finish();
DirectByteArrayOutputStream out = new DirectByteArrayOutputStream(length);
byte[] buffer = new byte[1024 * (length > 1024 * 124 ? 32 : 16)];
while (!deflater.finished()) {
out.write(buffer, 0, deflater.deflate(buffer));
}
out.close();
deflater.end();
return out;
}
// Paper end
}

View File

@@ -89,7 +89,8 @@ public class RegionFileCache {
}
private static final int DEFAULT_SIZE_THRESHOLD = 1024 * 8;
private static final int OVERZEALOUS_THRESHOLD = 1024 * 2;
private static final int OVERZEALOUS_TOTAL_THRESHOLD = 1024 * 64;
private static final int OVERZEALOUS_THRESHOLD = 1024;
private static int SIZE_THRESHOLD = DEFAULT_SIZE_THRESHOLD;
private static void resetFilterThresholds() {
SIZE_THRESHOLD = Math.max(1024 * 4, Integer.getInteger("Paper.FilterThreshhold", DEFAULT_SIZE_THRESHOLD));
@@ -97,6 +98,11 @@ public class RegionFileCache {
static {
resetFilterThresholds();
}
static boolean isOverzealous() {
return SIZE_THRESHOLD == OVERZEALOUS_THRESHOLD;
}
private static void writeRegion(File file, int x, int z, NBTTagCompound nbttagcompound) throws IOException {
RegionFile regionfile = getRegionFile(file, x, z);
@@ -146,11 +152,15 @@ public class RegionFileCache {
private static void filterChunkList(NBTTagCompound level, NBTTagCompound extra, String key) {
NBTTagList list = level.getList(key, 10);
NBTTagList newList = extra.getList(key, 10);
int totalSize = 0;
for (Iterator<NBTBase> iterator = list.list.iterator(); iterator.hasNext(); ) {
NBTBase object = iterator.next();
if (getNBTSize(object) > SIZE_THRESHOLD) {
int nbtSize = getNBTSize(object);
if (nbtSize > SIZE_THRESHOLD || (SIZE_THRESHOLD == OVERZEALOUS_THRESHOLD && totalSize > OVERZEALOUS_TOTAL_THRESHOLD)) {
newList.add(object);
iterator.remove();
} else {
totalSize += nbtSize;
}
}
level.set(key, list);

View File

@@ -114,7 +114,7 @@ public final class SpawnerCreature {
if ((!enumcreaturetype.c() || flag1) && (enumcreaturetype.c() || flag) && (!enumcreaturetype.d() || flag2)) {
k = limit * i / SpawnerCreature.b; // CraftBukkit - use per-world limits
int l1 = worldserver.entityList.getCreatureCount(enumcreaturetype); // Paper - entity count cache
int l1 = ((com.destroystokyo.paper.PaperWorldEntityList) worldserver.entityList).getCreatureCount(enumcreaturetype); // Paper - entity count cache
if (l1 <= k) {
BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition();

View File

@@ -53,7 +53,7 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
private static final EnumDirection[] a = EnumDirection.values();
private int b = 63;
// Spigot start - guard entity list from removals
public final com.destroystokyo.paper.PaperWorldEntityList entityList = new com.destroystokyo.paper.PaperWorldEntityList(this);
public final List<Entity> entityList = new com.destroystokyo.paper.PaperWorldEntityList(this);
/* // Paper start
{
@Override
@@ -1235,11 +1235,10 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
int i = entity.chunkX;
int j = entity.chunkZ;
if (entity.inChunk && this.isChunkLoaded(i, j, true)) {
this.getChunkAt(i, j).b(entity);
}
Chunk chunk = entity.getCurrentChunk(); // Paper
if (chunk != null) chunk.removeEntity(entity); // Paper
entity.shouldBeRemoved = true; // Paper
entityList.updateEntityCount(entity, -1); // Paper
((com.destroystokyo.paper.PaperWorldEntityList) entityList).updateEntityCount(entity, -1); // Paper
if (!guardEntityList) { // Spigot - It will get removed after the tick if we are ticking // Paper - always remove from current chunk above
// CraftBukkit start - Decrement loop variable field if we've already ticked this entity
@@ -1329,20 +1328,17 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
int j;
// Paper start - Set based removal lists
for (Entity e : this.g) {
/*
j = e.getChunkZ();
int k = e.getChunkX();
for (Iterator<Entity> it = this.g.iterator(); it.hasNext() ; ) {
entity = it.next(); // Paper
int k = entity.chunkX;
if (e.inChunk && this.isChunkLoaded(k, j, true)) {
this.getChunkAt(k, j).b(e);
}*/
Chunk chunk = e.inChunk ? e.getCurrentChunk() : null;
if (chunk != null) chunk.removeEntity(e);
}
j = entity.chunkZ;
Chunk chunk = entity.getCurrentChunk(); // Paper
if (chunk != null) chunk.removeEntity(entity); // Paper
//} // Paper - merge
for (Entity e : this.g) {
this.c(e);
//for (Entity e : this.g) { // Paper - merge
this.c(entity); // Paper use entity
}
// Paper end
@@ -1399,15 +1395,11 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
//this.methodProfiler.exit(); // Akarin - remove caller
//this.methodProfiler.enter(* // Akarin - remove caller
if (entity.dead) {
// Paper start
/*
j = entity.chunkX;
int l = entity.chunkZ;
if (entity.inChunk && this.isChunkLoaded(j, l, true)) {
this.getChunkAt(j, l).b(entity);
}*/
Chunk chunk = entity.inChunk ? entity.getCurrentChunk() : null;
// Paper start
Chunk chunk = entity.getCurrentChunk();
if (chunk != null) chunk.removeEntity(entity);
// Paper end
@@ -1988,12 +1980,13 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
return null;
} else {
// CraftBukkit start
if (capturedTileEntities.containsKey(blockposition)) {
return capturedTileEntities.get(blockposition);
TileEntity tileentity = null; // Paper
if (!capturedTileEntities.isEmpty() && (tileentity = capturedTileEntities.get(blockposition)) != null) { // Paper
return tileentity; // Paper
}
// CraftBukkit end
TileEntity tileentity = null;
//TileEntity tileentity = null; // Paper - move up
if (this.J) {
tileentity = this.E(blockposition);

View File

@@ -39,6 +39,7 @@ public class WorldPersistentData {
@Nullable
public <T extends PersistentBase> T a(Function<String, T> function, String s) {
if ("Mineshaft_index".equals(s) || "Mineshaft".equals(s)) return null; // Paper - mineshaft is useless data
T persistentbase = (T) this.data.get(s); // Paper - decompile fix
if (persistentbase == null && this.e != null) {
@@ -49,14 +50,15 @@ public class WorldPersistentData {
persistentbase = function.apply(s); // Paper - decompile fix
persistentbase.a(a(this.e, this.b, s, 1631).getCompound("data"));
this.data.put(s, persistentbase);
}
} else this.data.put(s, NO_RESULT); // Paper
} catch (Exception exception) {
WorldPersistentData.a.error("Error loading saved data: {}", s, exception);
}
}
return persistentbase;
return persistentbase == NO_RESULT ? null : persistentbase; // Paper
}
private static final PersistentBase NO_RESULT = new ForcedChunk("chunks"); // Paper
public void a(String s, PersistentBase persistentbase) {
this.data.put(s, persistentbase);
@@ -126,6 +128,7 @@ public class WorldPersistentData {
}
public static NBTTagCompound a(IDataManager idatamanager, DimensionManager dimensionmanager, String s, int i) throws IOException {
if ("Mineshaft".equals(s) || "Mineshaft_index".equals(s)) return new NBTTagCompound(); // Paper
File file = idatamanager.getDataFile(dimensionmanager, s);
FileInputStream fileinputstream = new FileInputStream(file);
Throwable throwable = null;

View File

@@ -85,6 +85,7 @@ public class WorldServer extends World implements IAsyncTaskHandler {
this.P();
this.Q();
this.getWorldBorder().a(minecraftserver.au());
MCUtil.scheduleAsyncTask(() -> this.getChunkProvider().chunkLoader.getPersistentStructureLegacy(worldProvider.getDimensionManager(), worldMaps)); // Paper
}
public WorldServer i_() {

View File

@@ -30,6 +30,7 @@ import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.permissions.ServerOperator;
import org.bukkit.plugin.Plugin;
import org.bukkit.util.BoundingBox;
import org.bukkit.util.NumberConversions;
import org.bukkit.util.Vector;
public abstract class CraftEntity implements org.bukkit.entity.Entity {
@@ -330,6 +331,21 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
return entity.world.getWorld();
}
@Override
public void setRotation(float yaw, float pitch) {
NumberConversions.checkFinite(pitch, "pitch not finite");
NumberConversions.checkFinite(yaw, "yaw not finite");
yaw = Location.normalizeYaw(yaw);
pitch = Location.normalizePitch(pitch);
entity.yaw = yaw;
entity.pitch = pitch;
entity.lastYaw = yaw;
entity.lastPitch = pitch;
entity.setHeadRotation(yaw);
}
public boolean teleport(Location location) {
return teleport(location, TeleportCause.PLUGIN);
}

View File

@@ -697,6 +697,11 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
getHandle().playerConnection.sendPacket(packet);
}
@Override
public void setRotation(float yaw, float pitch) {
throw new UnsupportedOperationException("Cannot set rotation of players. Consider teleporting instead.");
}
@Override
public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) {
Preconditions.checkArgument(location != null, "location");

View File

@@ -437,7 +437,7 @@ public class CraftEventFactory {
} // Paper end - End iteration skip check - All tweaking ends here
}
// Spigot end
} else {
} else if (!(entity instanceof EntityPlayer)) {
event = CraftEventFactory.callEntitySpawnEvent(entity);
}

View File

@@ -1,5 +1,8 @@
package org.bukkit.craftbukkit.generator;
import java.util.HashSet;
import java.util.Set;
import net.minecraft.server.BlockPosition;
import net.minecraft.server.Blocks;
import net.minecraft.server.ChunkSection;
import net.minecraft.server.IBlockData;
@@ -17,6 +20,7 @@ import org.bukkit.material.MaterialData;
public final class CraftChunkData implements ChunkGenerator.ChunkData {
private final int maxHeight;
private final ChunkSection[] sections;
private Set<BlockPosition> tiles;
private World world; // Paper - Anti-Xray
public CraftChunkData(World world) {
@@ -142,6 +146,14 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData {
}
ChunkSection section = getChunkSection(y, true);
section.setType(x, y & 0xf, z, type);
if (type.getBlock().isTileEntity()) {
if (tiles == null) {
tiles = new HashSet<>();
}
tiles.add(new BlockPosition(x, y, z));
}
}
private ChunkSection getChunkSection(int y, boolean create) {
@@ -155,4 +167,8 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData {
ChunkSection[] getRawChunkData() {
return sections;
}
Set<BlockPosition> getTiles() {
return tiles;
}
}

View File

@@ -70,7 +70,8 @@ public class CustomChunkGenerator extends InternalChunkGenerator<GeneratorSettin
ChunkData data = generator.generateChunkData(this.world.getWorld(), random, x, z, biomegrid);
Preconditions.checkArgument(data instanceof CraftChunkData, "Plugins must use createChunkData(World) rather than implementing ChunkData: %s", data);
ChunkSection[] sections = ((CraftChunkData) data).getRawChunkData();
CraftChunkData craftData = (CraftChunkData) data;
ChunkSection[] sections = craftData.getRawChunkData();
ChunkSection[] csect = ichunkaccess.getSections();
int scnt = Math.min(csect.length, sections.length);
@@ -87,6 +88,20 @@ public class CustomChunkGenerator extends InternalChunkGenerator<GeneratorSettin
// Set biome grid
ichunkaccess.a(biomegrid.biome);
if (craftData.getTiles() != null) {
for (BlockPosition pos : craftData.getTiles()) {
int tx = pos.getX();
int ty = pos.getY();
int tz = pos.getZ();
Block block = craftData.getTypeId(tx, ty, tz).getBlock();
if (block.isTileEntity()) {
TileEntity tile = ((ITileEntity) block).a(world);
ichunkaccess.a(new BlockPosition((x << 4) + tx, ty, (z << 4) + tz), tile);
}
}
}
}
@Override

View File

@@ -95,6 +95,7 @@ public final class CraftItemStack extends ItemStack {
}
net.minecraft.server.ItemStack handle;
public net.minecraft.server.ItemStack getHandle() { return handle; } // Paper
/**
* Mirror

View File

@@ -66,6 +66,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Level;
@@ -1326,7 +1327,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable {
return;
}
Map<String, List<Object>> mods = new HashMap<>();
Map<String, List<Object>> mods = new LinkedHashMap<>();
for (Map.Entry<Attribute, AttributeModifier> entry : modifiers.entries()) {
if (entry.getKey() == null) {
continue;