--- a/net/minecraft/server/World.java +++ b/net/minecraft/server/World.java @@ -14,6 +14,17 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.util.Supplier; +// CraftBukkit start +import java.util.HashMap; +import java.util.Map; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.block.CapturedBlockState; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.event.block.BlockPhysicsEvent; +// CraftBukkit end + public abstract class World implements GeneratorAccess, AutoCloseable { protected static final Logger LOGGER = LogManager.getLogger(); @@ -40,7 +51,39 @@ private final WorldBorder worldBorder; private final BiomeManager biomeManager; - protected World(WorldData worlddata, DimensionManager dimensionmanager, BiFunction<World, WorldProvider, IChunkProvider> bifunction, GameProfilerFiller gameprofilerfiller, boolean flag) { + // CraftBukkit start Added the following + private final CraftWorld world; + public boolean pvpMode; + public boolean keepSpawnInMemory = true; + public org.bukkit.generator.ChunkGenerator generator; + + public boolean captureBlockStates = false; + public boolean captureTreeGeneration = false; + public Map<BlockPosition, CapturedBlockState> capturedBlockStates = new HashMap<>(); + public Map<BlockPosition, TileEntity> capturedTileEntities = new HashMap<>(); + public List<EntityItem> captureDrops; + public long ticksPerAnimalSpawns; + public long ticksPerMonsterSpawns; + public boolean populating; + + public CraftWorld getWorld() { + return this.world; + } + + public CraftServer getServer() { + return (CraftServer) Bukkit.getServer(); + } + + public Chunk getChunkIfLoaded(int x, int z) { + return ((ChunkProviderServer) this.chunkProvider).getChunkAt(x, z, false); + } + + protected World(WorldData worlddata, DimensionManager dimensionmanager, BiFunction<World, WorldProvider, IChunkProvider> bifunction, GameProfilerFiller gameprofilerfiller, boolean flag, org.bukkit.generator.ChunkGenerator gen, org.bukkit.World.Environment env) { + this.generator = gen; + this.world = new CraftWorld((WorldServer) this, gen, env); + this.ticksPerAnimalSpawns = this.getServer().getTicksPerAnimalSpawns(); // CraftBukkit + this.ticksPerMonsterSpawns = this.getServer().getTicksPerMonsterSpawns(); // CraftBukkit + // CraftBukkit end this.methodProfiler = gameprofilerfiller; this.worldData = worlddata; this.worldProvider = dimensionmanager.getWorldProvider(this); @@ -49,6 +92,35 @@ this.worldBorder = this.worldProvider.getWorldBorder(); this.serverThread = Thread.currentThread(); this.biomeManager = new BiomeManager(this, flag ? worlddata.getSeed() : WorldData.c(worlddata.getSeed()), dimensionmanager.getGenLayerZoomer()); + // CraftBukkit start + getWorldBorder().world = (WorldServer) this; + // From PlayerList.setPlayerFileData + getWorldBorder().a(new IWorldBorderListener() { + public void a(WorldBorder worldborder, double d0) { + getServer().getHandle().sendAll(new PacketPlayOutWorldBorder(worldborder, PacketPlayOutWorldBorder.EnumWorldBorderAction.SET_SIZE), worldborder.world); + } + + public void a(WorldBorder worldborder, double d0, double d1, long i) { + getServer().getHandle().sendAll(new PacketPlayOutWorldBorder(worldborder, PacketPlayOutWorldBorder.EnumWorldBorderAction.LERP_SIZE), worldborder.world); + } + + public void a(WorldBorder worldborder, double d0, double d1) { + getServer().getHandle().sendAll(new PacketPlayOutWorldBorder(worldborder, PacketPlayOutWorldBorder.EnumWorldBorderAction.SET_CENTER), worldborder.world); + } + + public void a(WorldBorder worldborder, int i) { + getServer().getHandle().sendAll(new PacketPlayOutWorldBorder(worldborder, PacketPlayOutWorldBorder.EnumWorldBorderAction.SET_WARNING_TIME), worldborder.world); + } + + public void b(WorldBorder worldborder, int i) { + getServer().getHandle().sendAll(new PacketPlayOutWorldBorder(worldborder, PacketPlayOutWorldBorder.EnumWorldBorderAction.SET_WARNING_BLOCKS), worldborder.world); + } + + public void b(WorldBorder worldborder, double d0) {} + + public void c(WorldBorder worldborder, double d0) {} + }); + // CraftBukkit end } @Override @@ -105,6 +177,17 @@ @Override public boolean setTypeAndData(BlockPosition blockposition, IBlockData iblockdata, int i) { + // CraftBukkit start - tree generation + if (this.captureTreeGeneration) { + CapturedBlockState blockstate = capturedBlockStates.get(blockposition); + if (blockstate == null) { + blockstate = CapturedBlockState.getTreeBlockState(this, blockposition, i); + this.capturedBlockStates.put(blockposition.immutableCopy(), blockstate); + } + blockstate.setData(iblockdata); + return true; + } + // CraftBukkit end if (isOutsideWorld(blockposition)) { return false; } else if (!this.isClientSide && this.worldData.getType() == WorldType.DEBUG_ALL_BLOCK_STATES) { @@ -112,9 +195,22 @@ } else { Chunk chunk = this.getChunkAtWorldCoords(blockposition); Block block = iblockdata.getBlock(); - IBlockData iblockdata1 = chunk.setType(blockposition, iblockdata, (i & 64) != 0); + + // CraftBukkit start - capture blockstates + if (this.captureBlockStates && !this.capturedBlockStates.containsKey(blockposition)) { + CapturedBlockState blockstate = CapturedBlockState.getBlockState(this, blockposition, i); + this.capturedBlockStates.put(blockposition.immutableCopy(), blockstate); + } + // CraftBukkit end + + IBlockData iblockdata1 = chunk.setType(blockposition, iblockdata, (i & 64) != 0, (i & 1024) == 0); // CraftBukkit custom NO_PLACE flag if (iblockdata1 == null) { + // CraftBukkit start - remove blockstate if failed + if (this.captureBlockStates) { + this.capturedBlockStates.remove(blockposition); + } + // CraftBukkit end return false; } else { IBlockData iblockdata2 = this.getType(blockposition); @@ -125,6 +221,7 @@ this.methodProfiler.exit(); } + /* if (iblockdata2 == iblockdata) { if (iblockdata1 != iblockdata2) { this.b(blockposition, iblockdata1, iblockdata2); @@ -151,12 +248,65 @@ this.a(blockposition, iblockdata1, iblockdata2); } + */ + + // CraftBukkit start + if (!this.captureBlockStates) { // Don't notify clients or update physics while capturing blockstates + // Modularize client and physic updates + notifyAndUpdatePhysics(blockposition, chunk, iblockdata1, iblockdata, iblockdata2, i); + } + // CraftBukkit end return true; } } } + // CraftBukkit start - Split off from above in order to directly send client and physic updates + public void notifyAndUpdatePhysics(BlockPosition blockposition, Chunk chunk, IBlockData oldBlock, IBlockData newBlock, IBlockData actualBlock, int i) { + IBlockData iblockdata = newBlock; + IBlockData iblockdata1 = oldBlock; + IBlockData iblockdata2 = actualBlock; + if (iblockdata2 == iblockdata) { + if (iblockdata1 != iblockdata2) { + this.b(blockposition, iblockdata1, iblockdata2); + } + + if ((i & 2) != 0 && (!this.isClientSide || (i & 4) == 0) && (this.isClientSide || chunk == null || (chunk.getState() != null && chunk.getState().isAtLeast(PlayerChunk.State.TICKING)))) { // allow chunk to be null here as chunk.isReady() is false when we send our notification during block placement + this.notify(blockposition, iblockdata1, iblockdata, i); + } + + if (!this.isClientSide && (i & 1) != 0) { + this.update(blockposition, iblockdata1.getBlock()); + if (iblockdata.isComplexRedstone()) { + this.updateAdjacentComparators(blockposition, newBlock.getBlock()); + } + } + + if ((i & 16) == 0) { + int j = i & -2; + + // CraftBukkit start + iblockdata1.b(this, blockposition, j); // Don't call an event for the old block to limit event spam + CraftWorld world = ((WorldServer) this).getWorld(); + if (world != null) { + BlockPhysicsEvent event = new BlockPhysicsEvent(world.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), CraftBlockData.fromData(iblockdata)); + this.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + } + // CraftBukkit end + iblockdata.a(this, blockposition, j); + iblockdata.b(this, blockposition, j); + } + + this.a(blockposition, iblockdata1, iblockdata2); + } + } + // CraftBukkit end + public void a(BlockPosition blockposition, IBlockData iblockdata, IBlockData iblockdata1) {} @Override @@ -195,6 +345,11 @@ @Override public void update(BlockPosition blockposition, Block block) { if (this.worldData.getType() != WorldType.DEBUG_ALL_BLOCK_STATES) { + // CraftBukkit start + if (populating) { + return; + } + // CraftBukkit end this.applyPhysics(blockposition, block); } @@ -243,6 +398,17 @@ IBlockData iblockdata = this.getType(blockposition); try { + // CraftBukkit start + CraftWorld world = ((WorldServer) this).getWorld(); + if (world != null) { + BlockPhysicsEvent event = new BlockPhysicsEvent(world.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), CraftBlockData.fromData(iblockdata), world.getBlockAt(blockposition1.getX(), blockposition1.getY(), blockposition1.getZ())); + this.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + } + // CraftBukkit end iblockdata.doPhysics(this, blockposition, block, blockposition1, false); } catch (Throwable throwable) { CrashReport crashreport = CrashReport.a(throwable, "Exception while updating neighbours"); @@ -285,6 +451,14 @@ @Override public IBlockData getType(BlockPosition blockposition) { + // CraftBukkit start - tree generation + if (captureTreeGeneration) { + CapturedBlockState previous = capturedBlockStates.get(blockposition); + if (previous != null) { + return previous.getHandle(); + } + } + // CraftBukkit end if (isOutsideWorld(blockposition)) { return Blocks.VOID_AIR.getBlockData(); } else { @@ -306,11 +480,11 @@ } public boolean isDay() { - return this.worldProvider.getDimensionManager() == DimensionManager.OVERWORLD && this.c < 4; + return this.worldProvider.getDimensionManager().getType() == DimensionManager.OVERWORLD && this.c < 4; // CraftBukkit } public boolean isNight() { - return this.worldProvider.getDimensionManager() == DimensionManager.OVERWORLD && !this.isDay(); + return this.worldProvider.getDimensionManager().getType() == DimensionManager.OVERWORLD && !this.isDay(); // CraftBukkit } @Override @@ -432,9 +606,11 @@ TileEntity tileentity1 = (TileEntity) this.tileEntityListPending.get(i); if (!tileentity1.isRemoved()) { + /* CraftBukkit start - Order matters, moved down if (!this.tileEntityList.contains(tileentity1)) { this.a(tileentity1); } + // CraftBukkit end */ if (this.isLoaded(tileentity1.getPosition())) { Chunk chunk = this.getChunkAtWorldCoords(tileentity1.getPosition()); @@ -442,6 +618,12 @@ chunk.setTileEntity(tileentity1.getPosition(), tileentity1); this.notify(tileentity1.getPosition(), iblockdata, iblockdata, 3); + // CraftBukkit start + // From above, don't screw this up - SPIGOT-1746 + if (!this.tileEntityList.contains(tileentity1)) { + this.a(tileentity1); + } + // CraftBukkit end } } } @@ -606,12 +788,25 @@ @Nullable @Override + // CraftBukkit start public TileEntity getTileEntity(BlockPosition blockposition) { + return getTileEntity(blockposition, true); + } + + @Nullable + protected TileEntity getTileEntity(BlockPosition blockposition, boolean validate) { + // CraftBukkit end if (isOutsideWorld(blockposition)) { return null; } else if (!this.isClientSide && Thread.currentThread() != this.serverThread) { return null; } else { + // CraftBukkit start + if (capturedTileEntities.containsKey(blockposition)) { + return capturedTileEntities.get(blockposition); + } + // CraftBukkit end + TileEntity tileentity = null; if (this.tickingTileEntities) { @@ -646,6 +841,13 @@ public void setTileEntity(BlockPosition blockposition, @Nullable TileEntity tileentity) { if (!isOutsideWorld(blockposition)) { if (tileentity != null && !tileentity.isRemoved()) { + // CraftBukkit start + if (captureBlockStates) { + tileentity.setLocation(this, blockposition); + capturedTileEntities.put(blockposition.immutableCopy(), tileentity); + return; + } + // CraftBukkit end if (this.tickingTileEntities) { tileentity.setLocation(this, blockposition); Iterator iterator = this.tileEntityListPending.iterator(); @@ -670,7 +872,7 @@ } public void removeTileEntity(BlockPosition blockposition) { - TileEntity tileentity = this.getTileEntity(blockposition); + TileEntity tileentity = this.getTileEntity(blockposition, false); // CraftBukkit if (tileentity != null && this.tickingTileEntities) { tileentity.ab_();