From 62ce9910f24159fc1a4a636b0f9b3c2025958d23 Mon Sep 17 00:00:00 2001 From: Sotr Date: Sun, 7 Apr 2019 02:20:40 +0800 Subject: [PATCH] Upstream Paper --- .../com/destroystokyo/paper/PaperCommand.java | 8 +- .../com/destroystokyo/paper/PaperConfig.java | 4 +- .../destroystokyo/paper/PaperWorldConfig.java | 22 +- src/main/java/net/minecraft/server/Chunk.java | 59 ++--- .../minecraft/server/ChunkRegionLoader.java | 5 +- .../java/net/minecraft/server/Entity.java | 15 +- .../minecraft/server/EntityFishingHook.java | 1 + .../minecraft/server/EntityInsentient.java | 1 + .../server/EntityMinecartAbstract.java | 2 +- .../net/minecraft/server/EntityPlayer.java | 48 +++- .../minecraft/server/EntityTrackerEntry.java | 2 +- .../java/net/minecraft/server/GameRules.java | 240 ++++++++++++++++++ .../net/minecraft/server/IChunkLoader.java | 1 + .../java/net/minecraft/server/ItemStack.java | 22 +- .../net/minecraft/server/LoginListener.java | 31 +++ .../net/minecraft/server/MinecraftServer.java | 7 + .../net/minecraft/server/PlayerInventory.java | 2 +- .../java/net/minecraft/server/RegionFile.java | 108 +++++--- .../net/minecraft/server/RegionFileCache.java | 14 +- .../net/minecraft/server/SpawnerCreature.java | 2 +- src/main/java/net/minecraft/server/World.java | 45 ++-- .../minecraft/server/WorldPersistentData.java | 7 +- .../net/minecraft/server/WorldServer.java | 1 + .../craftbukkit/entity/CraftEntity.java | 16 ++ .../craftbukkit/entity/CraftPlayer.java | 5 + .../craftbukkit/event/CraftEventFactory.java | 2 +- .../craftbukkit/generator/CraftChunkData.java | 16 ++ .../generator/CustomChunkGenerator.java | 17 +- .../craftbukkit/inventory/CraftItemStack.java | 1 + .../craftbukkit/inventory/CraftMetaItem.java | 3 +- src/test/java/org/bukkit/GameRuleTest.java | 2 +- 31 files changed, 574 insertions(+), 135 deletions(-) create mode 100644 src/main/java/net/minecraft/server/GameRules.java diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java index 9b9c8361e..8e1bda4de 100644 --- a/src/main/java/com/destroystokyo/paper/PaperCommand.java +++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java @@ -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; diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java index a7673dd49..9f240c35d 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -71,8 +71,8 @@ public class PaperConfig { commands = new HashMap(); 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); } diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java index fa1d88aa8..bfd690ecc 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -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; diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java index 2bcbf8902..46804203f 100644 --- a/src/main/java/net/minecraft/server/Chunk.java +++ b/src/main/java/net/minecraft/server/Chunk.java @@ -563,7 +563,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); @@ -712,40 +712,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 nextSlice = this.entitySlices[k]; // the next list to be added to + List 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 entitySlice = this.entitySlices[k]; - boolean inThis = entitySlice.contains(entity); - List 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 } @@ -753,7 +754,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); } @@ -767,17 +768,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 } @@ -1051,8 +1054,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) diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java index 53ae5d509..dce52ac0f 100644 --- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java +++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java @@ -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 } diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java index 0c58a4766..581c78e00 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java @@ -491,7 +491,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke this.aa(); } */ - this.checkAndDoHeightDamage(); + this.performVoidDamage(); // Paper end if (!this.world.isClientSide) { @@ -503,9 +503,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 @@ -577,7 +580,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(); } @@ -1864,7 +1867,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke } // Paper start - private java.lang.ref.WeakReference currentChunk = null; + java.lang.ref.WeakReference currentChunk = null; public void setCurrentChunk(Chunk chunk) { this.currentChunk = chunk != null ? new java.lang.ref.WeakReference<>(chunk) : null; diff --git a/src/main/java/net/minecraft/server/EntityFishingHook.java b/src/main/java/net/minecraft/server/EntityFishingHook.java index e1c685eed..afbf46842 100644 --- a/src/main/java/net/minecraft/server/EntityFishingHook.java +++ b/src/main/java/net/minecraft/server/EntityFishingHook.java @@ -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 } } diff --git a/src/main/java/net/minecraft/server/EntityInsentient.java b/src/main/java/net/minecraft/server/EntityInsentient.java index 3059682a4..d1ec201d8 100644 --- a/src/main/java/net/minecraft/server/EntityInsentient.java +++ b/src/main/java/net/minecraft/server/EntityInsentient.java @@ -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) { diff --git a/src/main/java/net/minecraft/server/EntityMinecartAbstract.java b/src/main/java/net/minecraft/server/EntityMinecartAbstract.java index f83a695a1..8e773edc5 100644 --- a/src/main/java/net/minecraft/server/EntityMinecartAbstract.java +++ b/src/main/java/net/minecraft/server/EntityMinecartAbstract.java @@ -200,7 +200,7 @@ public abstract class EntityMinecartAbstract extends Entity implements INamableT this.aa(); } */ - this.checkAndDoHeightDamage(); + this.performVoidDamage(); // Paper end int i; diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java index 1d3730083..b9d0c0f74 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java @@ -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 inv) { + List 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 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 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 diff --git a/src/main/java/net/minecraft/server/EntityTrackerEntry.java b/src/main/java/net/minecraft/server/EntityTrackerEntry.java index 2b8501143..5629f9909 100644 --- a/src/main/java/net/minecraft/server/EntityTrackerEntry.java +++ b/src/main/java/net/minecraft/server/EntityTrackerEntry.java @@ -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(); diff --git a/src/main/java/net/minecraft/server/GameRules.java b/src/main/java/net/minecraft/server/GameRules.java new file mode 100644 index 000000000..0bd5e02a2 --- /dev/null +++ b/src/main/java/net/minecraft/server/GameRules.java @@ -0,0 +1,240 @@ +package net.minecraft.server; + +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.arguments.BoolArgumentType; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import java.util.Iterator; +import java.util.Set; +import java.util.TreeMap; +import java.util.Map.Entry; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Supplier; +import javax.annotation.Nullable; + +public class GameRules { + + // Paper start - Optimize GameRules + private static final int RULES_SIZE = 256; + + private static java.util.LinkedHashMap linkedMapOf(final int capacity, final TreeMap map) { + final java.util.LinkedHashMap ret = new java.util.LinkedHashMap<>(capacity); + ret.putAll(map); + return ret; + } + + private static final java.util.LinkedHashMap 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)); + treemap.put("doMobSpawning", new GameRules.GameRuleDefinition("true", GameRules.EnumGameRuleType.BOOLEAN_VALUE)); + treemap.put("doMobLoot", new GameRules.GameRuleDefinition("true", GameRules.EnumGameRuleType.BOOLEAN_VALUE)); + treemap.put("doTileDrops", new GameRules.GameRuleDefinition("true", GameRules.EnumGameRuleType.BOOLEAN_VALUE)); + treemap.put("doEntityDrops", new GameRules.GameRuleDefinition("true", GameRules.EnumGameRuleType.BOOLEAN_VALUE)); + treemap.put("commandBlockOutput", new GameRules.GameRuleDefinition("true", GameRules.EnumGameRuleType.BOOLEAN_VALUE)); + treemap.put("naturalRegeneration", new GameRules.GameRuleDefinition("true", GameRules.EnumGameRuleType.BOOLEAN_VALUE)); + treemap.put("doDaylightCycle", new GameRules.GameRuleDefinition("true", GameRules.EnumGameRuleType.BOOLEAN_VALUE)); + treemap.put("logAdminCommands", new GameRules.GameRuleDefinition("true", GameRules.EnumGameRuleType.BOOLEAN_VALUE)); + treemap.put("showDeathMessages", new GameRules.GameRuleDefinition("true", GameRules.EnumGameRuleType.BOOLEAN_VALUE)); + treemap.put("randomTickSpeed", new GameRules.GameRuleDefinition("3", GameRules.EnumGameRuleType.NUMERICAL_VALUE)); + treemap.put("sendCommandFeedback", new GameRules.GameRuleDefinition("true", GameRules.EnumGameRuleType.BOOLEAN_VALUE)); + treemap.put("reducedDebugInfo", new GameRules.GameRuleDefinition("false", GameRules.EnumGameRuleType.BOOLEAN_VALUE, (minecraftserver, gamerules_gamerulevalue) -> { + int i = gamerules_gamerulevalue.b() ? 22 : 23; + Iterator iterator = minecraftserver.getPlayerList().v().iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityStatus(entityplayer, (byte) i)); + } + + })); + treemap.put("spectatorsGenerateChunks", new GameRules.GameRuleDefinition("true", GameRules.EnumGameRuleType.BOOLEAN_VALUE)); + treemap.put("spawnRadius", new GameRules.GameRuleDefinition("10", GameRules.EnumGameRuleType.NUMERICAL_VALUE)); + treemap.put("disableElytraMovementCheck", new GameRules.GameRuleDefinition("false", GameRules.EnumGameRuleType.BOOLEAN_VALUE)); + treemap.put("maxEntityCramming", new GameRules.GameRuleDefinition("24", GameRules.EnumGameRuleType.NUMERICAL_VALUE)); + treemap.put("doWeatherCycle", new GameRules.GameRuleDefinition("true", GameRules.EnumGameRuleType.BOOLEAN_VALUE)); + 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)); + })); // Paper - Optimize GameRules + private final java.util.LinkedHashMap b = new java.util.LinkedHashMap<>(RULES_SIZE); // Paper - Optimize GameRules + + public GameRules() { + Iterator iterator = GameRules.a.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + + this.b.put(entry.getKey(), ((GameRules.GameRuleDefinition) entry.getValue()).a()); + } + + } + + public void set(String s, String s1, @Nullable MinecraftServer minecraftserver) { + GameRules.GameRuleValue gamerules_gamerulevalue = (GameRules.GameRuleValue) this.b.get(s); + + if (gamerules_gamerulevalue != null) { + gamerules_gamerulevalue.a(s1, minecraftserver); + } + + } + + public boolean getBoolean(String s) { + GameRules.GameRuleValue gamerules_gamerulevalue = (GameRules.GameRuleValue) this.b.get(s); + + return gamerules_gamerulevalue != null ? gamerules_gamerulevalue.b() : false; + } + + public int c(String s) { + GameRules.GameRuleValue gamerules_gamerulevalue = (GameRules.GameRuleValue) this.b.get(s); + + return gamerules_gamerulevalue != null ? gamerules_gamerulevalue.c() : 0; + } + + public NBTTagCompound a() { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + Iterator iterator = this.b.keySet().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + GameRules.GameRuleValue gamerules_gamerulevalue = (GameRules.GameRuleValue) this.b.get(s); + + nbttagcompound.setString(s, gamerules_gamerulevalue.a()); + } + + return nbttagcompound; + } + + public void a(NBTTagCompound nbttagcompound) { + Set set = nbttagcompound.getKeys(); + Iterator iterator = set.iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + this.set(s, nbttagcompound.getString(s), (MinecraftServer) null); + } + + } + + public GameRules.GameRuleValue get(String s) { + return (GameRules.GameRuleValue) this.b.get(s); + } + + public static java.util.LinkedHashMap getGameRules() { // Paper - Optimize GameRules + return GameRules.a; + } + + public static enum EnumGameRuleType { + + ANY_VALUE(StringArgumentType::greedyString, (commandcontext, s) -> { + return (String) commandcontext.getArgument(s, String.class); + }), BOOLEAN_VALUE(BoolArgumentType::bool, (commandcontext, s) -> { + return ((Boolean) commandcontext.getArgument(s, Boolean.class)).toString(); + }), NUMERICAL_VALUE(IntegerArgumentType::integer, (commandcontext, s) -> { + return ((Integer) commandcontext.getArgument(s, Integer.class)).toString(); + }); + + private final Supplier> d; + private final BiFunction, String, String> e; + + private EnumGameRuleType(Supplier supplier, BiFunction, String, String> bifunction) { // Paper - decompile fix + this.d = supplier; + this.e = bifunction; + } + + public RequiredArgumentBuilder a(String s) { + return CommandDispatcher.a(s, (ArgumentType) this.d.get()); + } + + public void a(CommandContext commandcontext, String s, GameRules.GameRuleValue gamerules_gamerulevalue) { + gamerules_gamerulevalue.a((String) this.e.apply(commandcontext, s), ((CommandListenerWrapper) commandcontext.getSource()).getServer()); + } + } + + public static class GameRuleValue { + + private String a; + private boolean b; + private int c; + private double d; + private final GameRules.EnumGameRuleType e; + private final BiConsumer f; + + public GameRuleValue(String s, GameRules.EnumGameRuleType gamerules_enumgameruletype, BiConsumer biconsumer) { + this.e = gamerules_enumgameruletype; + this.f = biconsumer; + this.a(s, (MinecraftServer) null); + } + + public void a(String s, @Nullable MinecraftServer minecraftserver) { + this.a = s; + this.b = Boolean.parseBoolean(s); + this.c = this.b ? 1 : 0; + + try { + this.c = Integer.parseInt(s); + } catch (NumberFormatException numberformatexception) { + ; + } + + try { + this.d = Double.parseDouble(s); + } catch (NumberFormatException numberformatexception1) { + ; + } + + if (minecraftserver != null) { + this.f.accept(minecraftserver, this); + } + + } + + public String a() { + return this.a; + } + + public boolean b() { + return this.b; + } + + public int c() { + return this.c; + } + + public GameRules.EnumGameRuleType getType() { + return this.e; + } + } + + public static class GameRuleDefinition { + + private final GameRules.EnumGameRuleType a; + private final String b; + private final BiConsumer c; + + public GameRuleDefinition(String s, GameRules.EnumGameRuleType gamerules_enumgameruletype) { + this(s, gamerules_enumgameruletype, (minecraftserver, gamerules_gamerulevalue) -> { + }); + } + + public GameRuleDefinition(String s, GameRules.EnumGameRuleType gamerules_enumgameruletype, BiConsumer biconsumer) { + this.a = gamerules_enumgameruletype; + this.b = s; + this.c = biconsumer; + } + + public GameRules.GameRuleValue a() { + return new GameRules.GameRuleValue(this.b, this.a, this.c); + } + + public GameRules.EnumGameRuleType b() { + return this.a; + } + } +} diff --git a/src/main/java/net/minecraft/server/IChunkLoader.java b/src/main/java/net/minecraft/server/IChunkLoader.java index dfb45cc4e..431f4ab18 100644 --- a/src/main/java/net/minecraft/server/IChunkLoader.java +++ b/src/main/java/net/minecraft/server/IChunkLoader.java @@ -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 consumer) throws IOException; // Paper - Async Chunks @Nullable diff --git a/src/main/java/net/minecraft/server/ItemStack.java b/src/main/java/net/minecraft/server/ItemStack.java index f81baa87d..eb130c012 100644 --- a/src/main/java/net/minecraft/server/ItemStack.java +++ b/src/main/java/net/minecraft/server/ItemStack.java @@ -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 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 diff --git a/src/main/java/net/minecraft/server/LoginListener.java b/src/main/java/net/minecraft/server/LoginListener.java index dfe7a029f..503f66582 100644 --- a/src/main/java/net/minecraft/server/LoginListener.java +++ b/src/main/java/net/minecraft/server/LoginListener.java @@ -282,6 +282,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 { @@ -292,6 +322,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(); diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index b93fccf91..8db5c6a35 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -606,6 +606,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 } @@ -927,6 +928,7 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati co.aikar.timings.TimingsManager.FULL_SERVER_TICK.startTiming(); // Paper 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) { @@ -995,6 +997,11 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati PaperLightingQueue.processQueue(startTime); // Paper expiringMaps.removeIf(ExpiringMap::clean); // Paper this.slackActivityAccountant.tickEnded(l); // Spigot + // 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 } diff --git a/src/main/java/net/minecraft/server/PlayerInventory.java b/src/main/java/net/minecraft/server/PlayerInventory.java index 997fdc499..988a36119 100644 --- a/src/main/java/net/minecraft/server/PlayerInventory.java +++ b/src/main/java/net/minecraft/server/PlayerInventory.java @@ -20,7 +20,7 @@ public class PlayerInventory implements IInventory { public final NonNullList items; public final NonNullList armor; public final NonNullList extraSlots; - private final List> f; + private final List> f;List> getComponents() { return f; } // Paper - OBFHELPER public int itemInHandIndex; public EntityHuman player; private ItemStack carried; diff --git a/src/main/java/net/minecraft/server/RegionFile.java b/src/main/java/net/minecraft/server/RegionFile.java index d148ce497..e5659980b 100644 --- a/src/main/java/net/minecraft/server/RegionFile.java +++ b/src/main/java/net/minecraft/server/RegionFile.java @@ -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 f; + private List f; private List 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 + } diff --git a/src/main/java/net/minecraft/server/RegionFileCache.java b/src/main/java/net/minecraft/server/RegionFileCache.java index a17e76d83..17e76815a 100644 --- a/src/main/java/net/minecraft/server/RegionFileCache.java +++ b/src/main/java/net/minecraft/server/RegionFileCache.java @@ -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 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); diff --git a/src/main/java/net/minecraft/server/SpawnerCreature.java b/src/main/java/net/minecraft/server/SpawnerCreature.java index 17bb81c03..297c53d15 100644 --- a/src/main/java/net/minecraft/server/SpawnerCreature.java +++ b/src/main/java/net/minecraft/server/SpawnerCreature.java @@ -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(); diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java index 9faed9303..b940f95bd 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java @@ -47,7 +47,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 entityList = new com.destroystokyo.paper.PaperWorldEntityList(this); /* // Paper start { @Override @@ -1204,11 +1204,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 @@ -1288,20 +1287,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 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 @@ -1358,15 +1354,11 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc this.methodProfiler.exit(); this.methodProfiler.enter("remove"); 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 @@ -1943,12 +1935,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); diff --git a/src/main/java/net/minecraft/server/WorldPersistentData.java b/src/main/java/net/minecraft/server/WorldPersistentData.java index 8d51af286..e86d382c8 100644 --- a/src/main/java/net/minecraft/server/WorldPersistentData.java +++ b/src/main/java/net/minecraft/server/WorldPersistentData.java @@ -39,6 +39,7 @@ public class WorldPersistentData { @Nullable public T a(Function 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; diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java index a4fc1e5e2..ee071ba2f 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -80,6 +80,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_() { diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java index 51f70c5ee..a12dd4779 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -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); } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index 76d5eaa7f..eb5971ac1 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -693,6 +693,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"); diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java index 2e0b4de83..7905420ca 100644 --- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java @@ -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); } diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java index 923d1b282..29695da2e 100644 --- a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java +++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java @@ -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 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 getTiles() { + return tiles; + } } diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java index a299092a5..04c0adf7c 100644 --- a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java +++ b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java @@ -70,7 +70,8 @@ public class CustomChunkGenerator extends InternalChunkGenerator> mods = new HashMap<>(); + Map> mods = new LinkedHashMap<>(); for (Map.Entry entry : modifiers.entries()) { if (entry.getKey() == null) { continue; diff --git a/src/test/java/org/bukkit/GameRuleTest.java b/src/test/java/org/bukkit/GameRuleTest.java index 1ed0f4cf2..40edb8d66 100644 --- a/src/test/java/org/bukkit/GameRuleTest.java +++ b/src/test/java/org/bukkit/GameRuleTest.java @@ -21,7 +21,7 @@ public class GameRuleTest { @Test public void testMinecraftRules() { - TreeMap minecraftRules = GameRules.getGameRules(); + Map minecraftRules = GameRules.getGameRules(); // Paper - Optimize GameRules for (Map.Entry entry : minecraftRules.entrySet()) { GameRule bukkitRule = GameRule.getByName(entry.getKey());