From 4f6bcc84f9cc534388061a0b9d1335aed8c8bc92 Mon Sep 17 00:00:00 2001 From: DerFrZocker Date: Sun, 15 Aug 2021 08:08:16 +1000 Subject: [PATCH] SPIGOT-5880, SPIGOT-5567: New ChunkGenerator API ## **Current API** The current world generation API is very old and limited when you want to make more complex world generation. Resulting in some hard to fix bugs such as that you cannot modify blocks outside the chunk in the BlockPopulator (which should and was per the docs possible), or strange behavior such as SPIGOT-5880. ## **New API** With the new API, the generation is more separate in multiple methods and is more in line with Vanilla chunk generation. The new API is designed to as future proof as possible. If for example a new generation step is added it can easily also be added as a step in API by simply creating the method for it. On the other side if a generation step gets removed, the method can easily be called after another, which is the case with surface and bedrock. The new API and changes are also fully backwards compatible with old chunk generators. ### **Changes in the new api** **Extra generation steps:** Noise, surface, bedrock and caves are added as steps. With those generation steps three extra methods for Vanilla generation are also added. Those new methods provide the ChunkData instead of returning one. The reason for this is, that the ChunkData is now backed by a ChunkAccess. With this, each step has the information of the step before and the Vanilla information (if chosen by setting a 'should' method to true). The old method is deprecated. **New class BiomeProvider** The BiomeProvider acts as Biome source and wrapper for the NMS class WorldChunkManager. With this the underlying Vanilla ChunkGeneration knows which Biome to use for the structure and decoration generation. (Fixes: SPIGOT-5880). Although the List of Biomes which is required in BiomeProvider, is currently not much in use in Vanilla, I decided to add it to future proof the API when it may be required in later versions of Minecraft. The BiomeProvider is also separated from the ChunkGenerator for plugins which only want to change the biome map, such as single Biome worlds or if some biomes should be more present than others. **Deprecated isParallelCapable** Mojang has and is pushing to a more multi threaded chunk generation. This should also be the case for custom chunk generators. This is why the new API only supports multi threaded generation. This does not affect the old API, which is still checking this. **Base height method added** This method was added to also bring the Minecraft generator and Bukkit generator more in line. With this it is possible to return the max height of a location (before decorations). This is useful to let most structures know were to place them. This fixes SPIGOT-5567. (This fixes not all structures placement, desert pyramids for example are still way up at y-level 64, This however is more a vanilla bug and should be fixed at Mojangs end). **WorldInfo Class** The World object was swapped for a WorldInfo object. This is because many methods of the World object won't work during world generation and would mostly likely result in a deadlock. It contains any information a plugin should need to identify the world. **BlockPopulator Changes** Instead of directly manipulating a chunk, changes are now made to a new class LimitedRegion, this class provides methods to populated the chunk and its surrounding area. The wrapping is done so that the population can be moved into the place where Minecraft generates decorations. Where there is no chunk to access yet. By moving it into this place the generation is now async and the surrounding area of the chunk can also be used. For common methods between the World and LimitedRegion a RegionAccessor was added. --- .../minecraft/server/MinecraftServer.patch | 73 +- .../minecraft/server/level/WorldServer.patch | 4 +- .../net/minecraft/world/entity/Entity.patch | 73 +- .../net/minecraft/world/level/World.patch | 4 +- .../world/level/chunk/ChunkGenerator.patch | 68 +- .../levelgen/ChunkGeneratorAbstract.patch | 69 ++ .../bukkit/craftbukkit/CraftHeightMap.java | 2 +- .../craftbukkit/CraftRegionAccessor.java | 888 ++++++++++++++++++ .../org/bukkit/craftbukkit/CraftServer.java | 65 +- .../org/bukkit/craftbukkit/CraftWorld.java | 736 +-------------- .../bukkit/craftbukkit/block/CraftBarrel.java | 8 +- .../bukkit/craftbukkit/block/CraftBeacon.java | 3 + .../craftbukkit/block/CraftBeehive.java | 2 + .../bukkit/craftbukkit/block/CraftBlock.java | 6 +- .../block/CraftBlockEntityState.java | 5 +- .../craftbukkit/block/CraftBlockState.java | 41 +- .../bukkit/craftbukkit/block/CraftChest.java | 7 +- .../craftbukkit/block/CraftDispenser.java | 3 + .../craftbukkit/block/CraftDropper.java | 3 + .../craftbukkit/block/CraftJukebox.java | 7 +- .../craftbukkit/block/CraftLectern.java | 2 +- .../craftbukkit/block/CraftShulkerBox.java | 4 +- .../block/CraftStructureBlock.java | 12 +- .../craftbukkit/entity/CraftCreeper.java | 26 +- .../craftbukkit/entity/CraftEntity.java | 5 + .../craftbukkit/entity/CraftHanging.java | 2 +- .../craftbukkit/entity/CraftItemFrame.java | 9 +- .../craftbukkit/entity/CraftLivingEntity.java | 25 +- .../bukkit/craftbukkit/entity/CraftMob.java | 3 + .../craftbukkit/entity/CraftPainting.java | 2 +- .../entity/CraftShulkerBullet.java | 3 + .../craftbukkit/entity/CraftVillager.java | 2 + .../craftbukkit/generator/CraftChunkData.java | 110 ++- .../generator/CraftLimitedRegion.java | 186 ++++ .../craftbukkit/generator/CraftWorldInfo.java | 68 ++ .../generator/CustomChunkGenerator.java | 129 ++- .../generator/CustomWorldChunkManager.java | 58 ++ .../generator/OldCraftChunkData.java | 196 ++++ .../util/BlockStateListPopulator.java | 37 +- .../util/DummyGeneratorAccess.java | 25 +- .../craftbukkit/generator/ChunkDataTest.java | 12 +- 41 files changed, 2034 insertions(+), 949 deletions(-) create mode 100644 nms-patches/net/minecraft/world/level/levelgen/ChunkGeneratorAbstract.patch create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java create mode 100644 src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java create mode 100644 src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java create mode 100644 src/main/java/org/bukkit/craftbukkit/generator/CustomWorldChunkManager.java create mode 100644 src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java diff --git a/nms-patches/net/minecraft/server/MinecraftServer.patch b/nms-patches/net/minecraft/server/MinecraftServer.patch index 9c96cad32..74c2ebb17 100644 --- a/nms-patches/net/minecraft/server/MinecraftServer.patch +++ b/nms-patches/net/minecraft/server/MinecraftServer.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java -@@ -163,6 +163,25 @@ +@@ -163,6 +163,27 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -16,17 +16,19 @@ +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.server.dedicated.DedicatedServerProperties; +import net.minecraft.util.datafix.DataConverterRegistry; ++import net.minecraft.world.level.levelgen.ChunkGeneratorAbstract; +import net.minecraft.world.level.storage.WorldDataServer; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.Main; ++import org.bukkit.craftbukkit.generator.CustomWorldChunkManager; +import org.bukkit.event.server.ServerLoadEvent; +// CraftBukkit end + public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant implements IMojangStatistics, ICommandListener, AutoCloseable { public static final Logger LOGGER = LogManager.getLogger(); -@@ -254,6 +273,20 @@ +@@ -254,6 +275,20 @@ private final DefinedStructureManager structureManager; protected SaveData worldData; @@ -47,7 +49,7 @@ public static S a(Function function) { AtomicReference atomicreference = new AtomicReference(); Thread thread = new Thread(() -> { -@@ -263,14 +296,14 @@ +@@ -263,14 +298,14 @@ thread.setUncaughtExceptionHandler((thread1, throwable) -> { MinecraftServer.LOGGER.error(throwable); }); @@ -64,7 +66,7 @@ super("Server"); this.metricsRecorder = InactiveMetricsRecorder.INSTANCE; this.profiler = this.metricsRecorder.e(); -@@ -282,7 +315,7 @@ +@@ -282,7 +317,7 @@ this.status = new ServerPing(); this.random = new Random(); this.port = -1; @@ -73,7 +75,7 @@ this.running = true; this.tickTimes = new long[100]; this.resourcePack = ""; -@@ -312,13 +345,40 @@ +@@ -312,13 +347,40 @@ this.structureManager = new DefinedStructureManager(datapackresources.i(), convertable_conversionsession, datafixer); this.serverThread = thread; this.executor = SystemUtils.f(); @@ -115,7 +117,7 @@ ScoreboardServer scoreboardserver1 = this.getScoreboard(); Objects.requireNonNull(scoreboardserver1); -@@ -329,7 +389,7 @@ +@@ -329,7 +391,7 @@ public static void convertWorld(Convertable.ConversionSession convertable_conversionsession) { if (convertable_conversionsession.isConvertable()) { @@ -124,7 +126,7 @@ convertable_conversionsession.convert(new IProgressUpdate() { private long timeStamp = SystemUtils.getMonotonicMillis(); -@@ -358,48 +418,198 @@ +@@ -358,48 +420,211 @@ } @@ -154,8 +156,7 @@ + + overworldData = new WorldDataServer(worldsettings, generatorsettings, Lifecycle.stable()); + } - -- this.a(worldloadlistener); ++ + GeneratorSettings overworldSettings = overworldData.getGeneratorSettings(); + RegistryMaterials registrymaterials = overworldSettings.d(); + for (Entry, WorldDimension> entry : registrymaterials.d()) { @@ -163,7 +164,8 @@ + + WorldServer world; + int dimension = 0; -+ + +- this.a(worldloadlistener); + if (dimensionKey == WorldDimension.NETHER) { + if (getAllowNether()) { + dimension = -1; @@ -226,6 +228,7 @@ + } + + org.bukkit.generator.ChunkGenerator gen = this.server.getGenerator(name); ++ org.bukkit.generator.BiomeProvider biomeProvider = this.server.getBiomeProvider(name); + + WorldDataServer worlddata = (WorldDataServer) worldSession.a((DynamicOps) registryreadops, datapackconfiguration); + if (worlddata == null) { @@ -271,6 +274,18 @@ + chunkgenerator = worlddimension.c(); + } + ++ org.bukkit.generator.WorldInfo worldInfo = new org.bukkit.craftbukkit.generator.CraftWorldInfo(iworlddataserver, worldSession, org.bukkit.World.Environment.getEnvironment(dimension), dimensionmanager); ++ if (biomeProvider == null && gen != null) { ++ biomeProvider = gen.getDefaultBiomeProvider(worldInfo); ++ } ++ ++ if (biomeProvider != null) { ++ WorldChunkManager worldChunkManager = new CustomWorldChunkManager(worldInfo, biomeProvider, registryHolder.b(IRegistry.BIOME_REGISTRY)); ++ if (chunkgenerator instanceof ChunkGeneratorAbstract) { ++ chunkgenerator = new ChunkGeneratorAbstract(worldChunkManager, chunkgenerator.strongholdSeed, ((ChunkGeneratorAbstract) chunkgenerator).settings); ++ } ++ } ++ + ResourceKey worldKey = ResourceKey.a(IRegistry.DIMENSION_REGISTRY, dimensionKey.a()); + + if (dimensionKey == WorldDimension.OVERWORLD) { @@ -279,14 +294,14 @@ + + WorldLoadListener worldloadlistener = this.progressListenerFactory.create(11); + -+ world = new WorldServer(this, this.executor, worldSession, iworlddataserver, worldKey, dimensionmanager, worldloadlistener, chunkgenerator, flag, j, list, true, org.bukkit.World.Environment.getEnvironment(dimension), gen); ++ world = new WorldServer(this, this.executor, worldSession, iworlddataserver, worldKey, dimensionmanager, worldloadlistener, chunkgenerator, flag, j, list, true, org.bukkit.World.Environment.getEnvironment(dimension), gen, biomeProvider); + WorldPersistentData worldpersistentdata = world.getWorldPersistentData(); + this.initializeScoreboards(worldpersistentdata); + this.server.scoreboardManager = new org.bukkit.craftbukkit.scoreboard.CraftScoreboardManager(this, world.getScoreboard()); + this.commandStorage = new PersistentCommandStorage(worldpersistentdata); + } else { + WorldLoadListener worldloadlistener = this.progressListenerFactory.create(11); -+ world = new WorldServer(this, this.executor, worldSession, iworlddataserver, worldKey, dimensionmanager, worldloadlistener, chunkgenerator, flag, j, ImmutableList.of(), true, org.bukkit.World.Environment.getEnvironment(dimension), gen); ++ world = new WorldServer(this, this.executor, worldSession, iworlddataserver, worldKey, dimensionmanager, worldloadlistener, chunkgenerator, flag, j, ImmutableList.of(), true, org.bukkit.World.Environment.getEnvironment(dimension), gen, biomeProvider); + } + + worlddata.a(this.getServerModName(), this.getModded().isPresent()); @@ -354,7 +369,7 @@ if (!iworlddataserver.p()) { try { a(worldserver, iworlddataserver, generatorsettings.c(), flag); -@@ -421,31 +631,8 @@ +@@ -421,31 +646,8 @@ iworlddataserver.c(true); } @@ -387,7 +402,7 @@ private static void a(WorldServer worldserver, IWorldDataServer iworlddataserver, boolean flag, boolean flag1) { if (flag1) { -@@ -458,6 +645,21 @@ +@@ -458,6 +660,21 @@ return biomebase.b().b(); }, random); ChunkCoordIntPair chunkcoordintpair = blockposition == null ? new ChunkCoordIntPair(0, 0) : new ChunkCoordIntPair(blockposition); @@ -409,7 +424,7 @@ if (blockposition == null) { MinecraftServer.LOGGER.warn("Unable to find spawn biome"); -@@ -532,8 +734,15 @@ +@@ -532,8 +749,15 @@ iworlddataserver.setGameType(EnumGamemode.SPECTATOR); } @@ -427,7 +442,7 @@ MinecraftServer.LOGGER.info("Preparing start region for dimension {}", worldserver.getDimensionKey().a()); BlockPosition blockposition = worldserver.getSpawn(); -@@ -546,16 +755,20 @@ +@@ -546,16 +770,20 @@ chunkproviderserver.addTicket(TicketType.START, new ChunkCoordIntPair(blockposition), 11, Unit.INSTANCE); while (chunkproviderserver.b() != 441) { @@ -456,7 +471,7 @@ ForcedChunk forcedchunk = (ForcedChunk) worldserver1.getWorldPersistentData().a(ForcedChunk::b, "chunks"); if (forcedchunk != null) { -@@ -570,11 +783,18 @@ +@@ -570,11 +798,18 @@ } } @@ -478,7 +493,7 @@ } protected void loadResourcesZip() { -@@ -619,12 +839,16 @@ +@@ -619,12 +854,16 @@ worldserver.save((IProgressUpdate) null, flag1, worldserver.noSave && !flag2); } @@ -495,7 +510,7 @@ if (flag1) { Iterator iterator1 = this.getWorlds().iterator(); -@@ -645,8 +869,29 @@ +@@ -645,8 +884,29 @@ this.stop(); } @@ -525,7 +540,7 @@ if (this.getServerConnection() != null) { this.getServerConnection().b(); } -@@ -655,6 +900,7 @@ +@@ -655,6 +915,7 @@ MinecraftServer.LOGGER.info("Saving players"); this.playerList.savePlayers(); this.playerList.shutdown(); @@ -533,7 +548,7 @@ } MinecraftServer.LOGGER.info("Saving worlds"); -@@ -732,9 +978,10 @@ +@@ -732,9 +993,10 @@ while (this.running) { long i = SystemUtils.getMonotonicMillis() - this.nextTickTime; @@ -545,7 +560,7 @@ MinecraftServer.LOGGER.warn("Can't keep up! Is the server overloaded? Running {}ms or {} ticks behind", i, j); this.nextTickTime += j * 50L; this.lastOverloadWarning = this.nextTickTime; -@@ -745,6 +992,7 @@ +@@ -745,6 +1007,7 @@ this.debugCommandProfiler = new MinecraftServer.a(SystemUtils.getMonotonicNanos(), this.tickCount); } @@ -553,7 +568,7 @@ this.nextTickTime += 50L; this.bh(); this.profiler.enter("tick"); -@@ -790,6 +1038,12 @@ +@@ -790,6 +1053,12 @@ } catch (Throwable throwable1) { MinecraftServer.LOGGER.error("Exception stopping the server", throwable1); } finally { @@ -566,7 +581,7 @@ this.exit(); } -@@ -798,8 +1052,15 @@ +@@ -798,8 +1067,15 @@ } private boolean canSleepForTick() { @@ -583,7 +598,7 @@ protected void sleepForTick() { this.executeAll(); -@@ -908,7 +1169,7 @@ +@@ -908,7 +1184,7 @@ this.status.b().a(agameprofile); } @@ -592,7 +607,7 @@ MinecraftServer.LOGGER.debug("Autosave started"); this.profiler.enter("save"); this.playerList.savePlayers(); -@@ -938,22 +1199,39 @@ +@@ -938,22 +1214,39 @@ } public void b(BooleanSupplier booleansupplier) { @@ -632,7 +647,7 @@ this.profiler.enter("tick"); -@@ -1042,7 +1320,7 @@ +@@ -1042,7 +1335,7 @@ @DontObfuscate public String getServerModName() { @@ -641,7 +656,7 @@ } public SystemReport b(SystemReport systemreport) { -@@ -1414,16 +1692,17 @@ +@@ -1414,16 +1707,17 @@ public CompletableFuture a(Collection collection) { CompletableFuture completablefuture = CompletableFuture.supplyAsync(() -> { @@ -661,7 +676,7 @@ this.packRepository.a(collection); this.worldData.a(a(this.packRepository)); datapackresources.j(); -@@ -1768,6 +2047,22 @@ +@@ -1768,6 +2062,22 @@ } diff --git a/nms-patches/net/minecraft/server/level/WorldServer.patch b/nms-patches/net/minecraft/server/level/WorldServer.patch index 3de6fa4d9..d70ac9a38 100644 --- a/nms-patches/net/minecraft/server/level/WorldServer.patch +++ b/nms-patches/net/minecraft/server/level/WorldServer.patch @@ -47,9 +47,9 @@ + } + + // Add env and gen to constructor, WorldData -> WorldDataServer -+ public WorldServer(MinecraftServer minecraftserver, Executor executor, Convertable.ConversionSession convertable_conversionsession, IWorldDataServer iworlddataserver, ResourceKey resourcekey, DimensionManager dimensionmanager, WorldLoadListener worldloadlistener, ChunkGenerator chunkgenerator, boolean flag, long i, List list, boolean flag1, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen) { ++ public WorldServer(MinecraftServer minecraftserver, Executor executor, Convertable.ConversionSession convertable_conversionsession, IWorldDataServer iworlddataserver, ResourceKey resourcekey, DimensionManager dimensionmanager, WorldLoadListener worldloadlistener, ChunkGenerator chunkgenerator, boolean flag, long i, List list, boolean flag1, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) { + // Objects.requireNonNull(minecraftserver); // CraftBukkit - decompile error -+ super(iworlddataserver, resourcekey, dimensionmanager, minecraftserver::getMethodProfiler, false, flag, i, gen, env); ++ super(iworlddataserver, resourcekey, dimensionmanager, minecraftserver::getMethodProfiler, false, flag, i, gen, biomeProvider, env); + this.pvpMode = minecraftserver.getPVP(); + convertable = convertable_conversionsession; + uuid = WorldUUID.getUUID(convertable_conversionsession.levelPath.toFile()); diff --git a/nms-patches/net/minecraft/world/entity/Entity.patch b/nms-patches/net/minecraft/world/entity/Entity.patch index 5eb8a0909..9e56d8588 100644 --- a/nms-patches/net/minecraft/world/entity/Entity.patch +++ b/nms-patches/net/minecraft/world/entity/Entity.patch @@ -59,13 +59,14 @@ protected static final Logger LOGGER = LogManager.getLogger(); public static final String ID_TAG = "id"; public static final String PASSENGERS_TAG = "Passengers"; -@@ -224,6 +274,21 @@ +@@ -224,6 +274,22 @@ private float crystalSoundIntensity; private int lastCrystalSoundPlayTick; public boolean hasVisualFire; + // CraftBukkit start + public boolean persist = true; + public boolean valid; ++ public boolean generation; + public org.bukkit.projectiles.ProjectileSource projectileSource; // For projectiles only + public boolean forceExplosionKnockback; // SPIGOT-949 + public boolean persistentInvisibility = false; @@ -81,7 +82,7 @@ public Entity(EntityTypes entitytypes, World world) { this.id = Entity.ENTITY_COUNTER.incrementAndGet(); -@@ -359,6 +424,12 @@ +@@ -359,6 +425,12 @@ public void ae() {} public void setPose(EntityPose entitypose) { @@ -94,7 +95,7 @@ this.entityData.set(Entity.DATA_POSE, entitypose); } -@@ -375,6 +446,33 @@ +@@ -375,6 +447,33 @@ } protected void setYawPitch(float f, float f1) { @@ -128,7 +129,7 @@ this.setYRot(f % 360.0F); this.setXRot(f1 % 360.0F); } -@@ -416,6 +514,15 @@ +@@ -416,6 +515,15 @@ this.entityBaseTick(); } @@ -144,7 +145,7 @@ public void entityBaseTick() { this.level.getMethodProfiler().enter("entityBaseTick"); if (this.isPassenger() && this.getVehicle().isRemoved()) { -@@ -429,7 +536,7 @@ +@@ -429,7 +537,7 @@ this.walkDistO = this.walkDist; this.xRotO = this.getXRot(); this.yRotO = this.getYRot(); @@ -153,7 +154,7 @@ if (this.aV()) { this.aW(); } -@@ -507,7 +614,23 @@ +@@ -507,7 +615,23 @@ public void burnFromLava() { if (!this.isFireProof()) { @@ -178,7 +179,7 @@ if (this.damageEntity(DamageSource.LAVA, 4.0F)) { this.playSound(SoundEffects.GENERIC_BURN, 0.4F, 2.0F + this.random.nextFloat() * 0.4F); } -@@ -516,6 +639,22 @@ +@@ -516,6 +640,22 @@ } public void setOnFire(int i) { @@ -201,7 +202,7 @@ int j = i * 20; if (this instanceof EntityLiving) { -@@ -614,6 +753,28 @@ +@@ -614,6 +754,28 @@ block.a((IBlockAccess) this.level, this); } @@ -230,15 +231,15 @@ if (this.onGround && !this.bE()) { block.stepOn(this.level, blockposition, iblockdata, this); } -@@ -1276,6 +1437,7 @@ +@@ -1276,6 +1438,7 @@ this.yo = d1; this.zo = d4; this.setPosition(d3, d1, d4); -+ level.getChunkAt((int) Math.floor(this.locX()) >> 4, (int) Math.floor(this.locZ()) >> 4); // CraftBukkit ++ if (valid) level.getChunkAt((int) Math.floor(this.locX()) >> 4, (int) Math.floor(this.locZ()) >> 4); // CraftBukkit } public void d(Vec3D vec3d) { -@@ -1466,6 +1628,12 @@ +@@ -1466,6 +1629,12 @@ return false; } @@ -251,7 +252,7 @@ public void a(Entity entity, int i, DamageSource damagesource) { if (entity instanceof EntityPlayer) { CriterionTriggers.ENTITY_KILLED_PLAYER.a((EntityPlayer) entity, this, damagesource); -@@ -1499,7 +1667,7 @@ +@@ -1499,7 +1668,7 @@ } else { String s = this.getSaveID(); @@ -260,7 +261,7 @@ return false; } else { nbttagcompound.setString("id", s); -@@ -1524,6 +1692,18 @@ +@@ -1524,6 +1693,18 @@ Vec3D vec3d = this.getMot(); nbttagcompound.set("Motion", this.newDoubleList(vec3d.x, vec3d.y, vec3d.z)); @@ -279,7 +280,7 @@ nbttagcompound.set("Rotation", this.newFloatList(this.getYRot(), this.getXRot())); nbttagcompound.setFloat("FallDistance", this.fallDistance); nbttagcompound.setShort("Fire", (short) this.remainingFireTicks); -@@ -1532,6 +1712,18 @@ +@@ -1532,6 +1713,18 @@ nbttagcompound.setBoolean("Invulnerable", this.invulnerable); nbttagcompound.setInt("PortalCooldown", this.portalCooldown); nbttagcompound.a("UUID", this.getUniqueID()); @@ -298,7 +299,7 @@ IChatBaseComponent ichatbasecomponent = this.getCustomName(); if (ichatbasecomponent != null) { -@@ -1599,6 +1791,11 @@ +@@ -1599,6 +1792,11 @@ } } @@ -310,7 +311,7 @@ return nbttagcompound; } catch (Throwable throwable) { CrashReport crashreport = CrashReport.a(throwable, "Saving entity NBT"); -@@ -1680,6 +1877,49 @@ +@@ -1680,6 +1878,49 @@ } else { throw new IllegalStateException("Entity has invalid position"); } @@ -360,7 +361,7 @@ } catch (Throwable throwable) { CrashReport crashreport = CrashReport.a(throwable, "Loading entity NBT"); CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being loaded"); -@@ -1755,9 +1995,22 @@ +@@ -1755,9 +1996,22 @@ } else if (this.level.isClientSide) { return null; } else { @@ -383,7 +384,7 @@ this.level.addEntity(entityitem); return entityitem; } -@@ -1849,7 +2102,7 @@ +@@ -1849,7 +2103,7 @@ this.setPose(EntityPose.STANDING); this.vehicle = entity; @@ -392,7 +393,7 @@ entity.n().filter((entity2) -> { return entity2 instanceof EntityPlayer; }).forEach((entity2) -> { -@@ -1880,7 +2133,7 @@ +@@ -1880,7 +2134,7 @@ Entity entity = this.vehicle; this.vehicle = null; @@ -401,7 +402,7 @@ } } -@@ -1889,10 +2142,31 @@ +@@ -1889,10 +2143,31 @@ this.bo(); } @@ -434,7 +435,7 @@ if (this.passengers.isEmpty()) { this.passengers = ImmutableList.of(entity); } else { -@@ -1908,12 +2182,32 @@ +@@ -1908,12 +2183,32 @@ } } @@ -468,7 +469,7 @@ if (this.passengers.size() == 1 && this.passengers.get(0) == entity) { this.passengers = ImmutableList.of(); } else { -@@ -1924,6 +2218,7 @@ +@@ -1924,6 +2219,7 @@ entity.boardingCooldown = 60; } @@ -476,7 +477,7 @@ } protected boolean o(Entity entity) { -@@ -1974,14 +2269,20 @@ +@@ -1974,14 +2270,20 @@ if (this.isInsidePortal) { MinecraftServer minecraftserver = worldserver.getMinecraftServer(); @@ -500,12 +501,12 @@ this.level.getMethodProfiler().exit(); } -@@ -2099,6 +2400,13 @@ +@@ -2099,6 +2401,13 @@ } public void setSwimming(boolean flag) { + // CraftBukkit start -+ if (this.isSwimming() != flag && this instanceof EntityLiving) { ++ if (valid && this.isSwimming() != flag && this instanceof EntityLiving) { + if (CraftEventFactory.callToggleSwimEvent((EntityLiving) this, flag).isCancelled()) { + return; + } @@ -514,7 +515,7 @@ this.setFlag(4, flag); } -@@ -2147,8 +2455,12 @@ +@@ -2147,8 +2456,12 @@ return this.getScoreboardTeam() != null ? this.getScoreboardTeam().isAlly(scoreboardteambase) : false; } @@ -528,7 +529,7 @@ } public boolean getFlag(int i) { -@@ -2175,7 +2487,17 @@ +@@ -2175,7 +2488,17 @@ } public void setAirTicks(int i) { @@ -547,7 +548,7 @@ } public int getTicksFrozen() { -@@ -2202,11 +2524,41 @@ +@@ -2202,11 +2525,41 @@ public void onLightningStrike(WorldServer worldserver, EntityLightning entitylightning) { this.setFireTicks(this.remainingFireTicks + 1); @@ -591,7 +592,7 @@ } public void k(boolean flag) { -@@ -2356,15 +2708,32 @@ +@@ -2356,15 +2709,32 @@ @Nullable public Entity b(WorldServer worldserver) { @@ -626,7 +627,7 @@ this.level.getMethodProfiler().exitEnter("reloading"); Entity entity = this.getEntityType().a((World) worldserver); -@@ -2373,9 +2742,17 @@ +@@ -2373,9 +2743,17 @@ entity.setPositionRotation(shapedetectorshape.pos.x, shapedetectorshape.pos.y, shapedetectorshape.pos.z, shapedetectorshape.yRot, entity.getXRot()); entity.setMot(shapedetectorshape.speed); worldserver.addEntityTeleport(entity); @@ -646,7 +647,7 @@ } this.cc(); -@@ -2396,13 +2773,18 @@ +@@ -2396,13 +2774,18 @@ @Nullable protected ShapeDetectorShape a(WorldServer worldserver) { @@ -669,7 +670,7 @@ return null; } else { WorldBorder worldborder = worldserver.getWorldBorder(); -@@ -2412,8 +2794,16 @@ +@@ -2412,8 +2795,16 @@ double d3 = Math.min(2.9999872E7D, worldborder.h() - 16.0D); double d4 = DimensionManager.a(this.level.getDimensionManager(), worldserver.getDimensionManager()); BlockPosition blockposition = new BlockPosition(MathHelper.a(this.locX() * d4, d0, d2), this.locY(), MathHelper.a(this.locZ() * d4, d1, d3)); @@ -687,7 +688,7 @@ IBlockData iblockdata = this.level.getType(this.portalEntrancePos); EnumDirection.EnumAxis enumdirection_enumaxis; Vec3D vec3d; -@@ -2430,8 +2820,8 @@ +@@ -2430,8 +2821,8 @@ vec3d = new Vec3D(0.5D, 0.0D, 0.0D); } @@ -698,7 +699,7 @@ } } else { BlockPosition blockposition1; -@@ -2441,8 +2831,15 @@ +@@ -2441,8 +2832,15 @@ } else { blockposition1 = worldserver.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES, worldserver.getSpawn()); } @@ -715,7 +716,7 @@ } } -@@ -2450,8 +2847,23 @@ +@@ -2450,8 +2848,23 @@ return BlockPortalShape.a(blockutil_rectangle, enumdirection_enumaxis, this.getPositionVector(), this.a(this.getPose())); } @@ -741,7 +742,7 @@ } public boolean canPortal() { -@@ -2660,7 +3072,26 @@ +@@ -2660,7 +3073,26 @@ } public final void a(AxisAlignedBB axisalignedbb) { diff --git a/nms-patches/net/minecraft/world/level/World.patch b/nms-patches/net/minecraft/world/level/World.patch index 9406c51bf..edabea486 100644 --- a/nms-patches/net/minecraft/world/level/World.patch +++ b/nms-patches/net/minecraft/world/level/World.patch @@ -66,9 +66,9 @@ + return typeKey; + } + -+ protected World(WorldDataMutable worlddatamutable, ResourceKey resourcekey, final DimensionManager dimensionmanager, Supplier supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.World.Environment env) { ++ protected World(WorldDataMutable worlddatamutable, ResourceKey resourcekey, final DimensionManager dimensionmanager, Supplier supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env) { + this.generator = gen; -+ this.world = new CraftWorld((WorldServer) this, gen, env); ++ this.world = new CraftWorld((WorldServer) this, gen, biomeProvider, env); + this.ticksPerAnimalSpawns = this.getCraftServer().getTicksPerAnimalSpawns(); // CraftBukkit + this.ticksPerMonsterSpawns = this.getCraftServer().getTicksPerMonsterSpawns(); // CraftBukkit + this.ticksPerWaterSpawns = this.getCraftServer().getTicksPerWaterSpawns(); // CraftBukkit diff --git a/nms-patches/net/minecraft/world/level/chunk/ChunkGenerator.patch b/nms-patches/net/minecraft/world/level/chunk/ChunkGenerator.patch index ca7d4eebc..04283df2f 100644 --- a/nms-patches/net/minecraft/world/level/chunk/ChunkGenerator.patch +++ b/nms-patches/net/minecraft/world/level/chunk/ChunkGenerator.patch @@ -1,6 +1,70 @@ --- a/net/minecraft/world/level/chunk/ChunkGenerator.java +++ b/net/minecraft/world/level/chunk/ChunkGenerator.java -@@ -269,7 +269,16 @@ +@@ -63,7 +63,7 @@ + protected final WorldChunkManager biomeSource; + protected final WorldChunkManager runtimeBiomeSource; + private final StructureSettings settings; +- private final long strongholdSeed; ++ public final long strongholdSeed; // PAIL private -> public + private final List strongholdPositions; + private final BaseStoneSource defaultBaseStoneSource; + +@@ -217,7 +217,7 @@ + } + } + +- public void addDecorations(RegionLimitedWorldAccess regionlimitedworldaccess, StructureManager structuremanager) { ++ public void addVanillaDecorations(RegionLimitedWorldAccess regionlimitedworldaccess, StructureManager structuremanager) { // CraftBukkit + ChunkCoordIntPair chunkcoordintpair = regionlimitedworldaccess.a(); + int i = chunkcoordintpair.d(); + int j = chunkcoordintpair.e(); +@@ -236,8 +236,45 @@ + } + } + ++ public void addDecorations(RegionLimitedWorldAccess regionlimitedworldaccess, StructureManager structuremanager) { ++ // CraftBukkit start ++ addDecorations(regionlimitedworldaccess, structuremanager, true); ++ } ++ ++ public void addDecorations(RegionLimitedWorldAccess regionlimitedworldaccess, StructureManager structuremanager, boolean vanilla) { ++ if (vanilla) { ++ addVanillaDecorations(regionlimitedworldaccess, structuremanager); ++ } ++ ++ org.bukkit.World world = regionlimitedworldaccess.getMinecraftWorld().getWorld(); ++ // only call when a populator is present (prevents unnecessary entity conversion) ++ if (world.getPopulators().size() != 0) { ++ org.bukkit.craftbukkit.generator.CraftLimitedRegion limitedRegion = new org.bukkit.craftbukkit.generator.CraftLimitedRegion(regionlimitedworldaccess); ++ int x = regionlimitedworldaccess.a().x; ++ int z = regionlimitedworldaccess.a().z; ++ for (org.bukkit.generator.BlockPopulator populator : world.getPopulators()) { ++ SeededRandom seededrandom = new SeededRandom(); ++ seededrandom.a(regionlimitedworldaccess.getSeed(), x, z); ++ populator.populate(world, seededrandom, x, z, limitedRegion); ++ } ++ limitedRegion.saveEntities(); ++ limitedRegion.breakLink(); ++ } ++ // CraftBukkit end ++ } ++ + public abstract void buildBase(RegionLimitedWorldAccess regionlimitedworldaccess, IChunkAccess ichunkaccess); + ++ // CraftBukkit start - spilt surface and bedrock generation code ++ public SeededRandom buildSurface(RegionLimitedWorldAccess regionlimitedworldaccess, IChunkAccess iChunkAccess) { ++ throw new UnsupportedOperationException("Methode not overridden"); ++ } ++ ++ public void buildBedrock(IChunkAccess iChunkAccess, Random random) { ++ throw new UnsupportedOperationException("Methode not overridden"); ++ } ++ // CraftBukkit end ++ + public void addMobs(RegionLimitedWorldAccess regionlimitedworldaccess) {} + + public StructureSettings getSettings() { +@@ -269,7 +306,16 @@ while (iterator.hasNext()) { Supplier> supplier = (Supplier) iterator.next(); @@ -18,7 +82,7 @@ } } -@@ -364,9 +373,11 @@ +@@ -364,9 +410,11 @@ } static { diff --git a/nms-patches/net/minecraft/world/level/levelgen/ChunkGeneratorAbstract.patch b/nms-patches/net/minecraft/world/level/levelgen/ChunkGeneratorAbstract.patch new file mode 100644 index 000000000..c6afa323f --- /dev/null +++ b/nms-patches/net/minecraft/world/level/levelgen/ChunkGeneratorAbstract.patch @@ -0,0 +1,69 @@ +--- a/net/minecraft/world/level/levelgen/ChunkGeneratorAbstract.java ++++ b/net/minecraft/world/level/levelgen/ChunkGeneratorAbstract.java +@@ -76,7 +76,7 @@ + protected final IBlockData defaultBlock; + protected final IBlockData defaultFluid; + private final long seed; +- protected final Supplier settings; ++ public final Supplier settings; // PAIL protected -> public + private final int height; + private final NoiseSampler sampler; + private final BaseStoneSource baseStoneSource; +@@ -257,6 +257,19 @@ + + @Override + public void buildBase(RegionLimitedWorldAccess regionlimitedworldaccess, IChunkAccess ichunkaccess) { ++ // CraftBukkit start - spilt surface and bedrock generation code ++ Random random = buildSurface(regionlimitedworldaccess, ichunkaccess); ++ buildBedrock(ichunkaccess, random); ++ } ++ ++ @Override ++ public void buildBedrock(IChunkAccess iChunkAccess, Random random) { ++ a(iChunkAccess, random); // PAIL rename setBedrock ++ } ++ ++ @Override ++ public SeededRandom buildSurface(RegionLimitedWorldAccess regionlimitedworldaccess, IChunkAccess ichunkaccess) { ++ // CraftBukkit end + ChunkCoordIntPair chunkcoordintpair = ichunkaccess.getPos(); + int i = chunkcoordintpair.x; + int j = chunkcoordintpair.z; +@@ -281,7 +294,10 @@ + } + } + +- this.a(ichunkaccess, seededrandom); ++ // CraftBukkit start - spilt surface and bedrock generation code ++ // this.a(ichunkaccess, seededrandom); ++ return seededrandom; ++ // CraftBukkit end + } + + private void a(IChunkAccess ichunkaccess, Random random) { +@@ -403,16 +419,23 @@ + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + + for (int i1 = 0; i1 < this.cellCountX; ++i1) { ++ // CraftBukkit start - decompile error ++ int i1Final = i1; + list.forEach((noiseinterpolator1) -> { +- noiseinterpolator1.a(i1); ++ noiseinterpolator1.a(i1Final); ++ // CraftBukkit end + }); + + for (int j1 = 0; j1 < this.cellCountZ; ++j1) { + ChunkSection chunksection = ichunkaccess.b(ichunkaccess.getSectionsCount() - 1); + + for (int k1 = j - 1; k1 >= 0; --k1) { ++ // CraftBukkit start - decompile error ++ int kiFinal = k1; ++ int j1Final = j1; + list.forEach((noiseinterpolator1) -> { +- noiseinterpolator1.a(k1, j1); ++ noiseinterpolator1.a(kiFinal, j1Final); ++ // CraftBukkit end + }); + + for (int l1 = this.cellHeight - 1; l1 >= 0; --l1) { diff --git a/src/main/java/org/bukkit/craftbukkit/CraftHeightMap.java b/src/main/java/org/bukkit/craftbukkit/CraftHeightMap.java index 55ec590f4..e635e0858 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftHeightMap.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftHeightMap.java @@ -2,7 +2,7 @@ package org.bukkit.craftbukkit; import org.bukkit.HeightMap; -final class CraftHeightMap { +public final class CraftHeightMap { private CraftHeightMap() { } diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java new file mode 100644 index 000000000..039e5dfd7 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java @@ -0,0 +1,888 @@ +package org.bukkit.craftbukkit; + +import com.google.common.base.Preconditions; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import net.minecraft.core.BlockPosition; +import net.minecraft.core.EnumDirection; +import net.minecraft.core.IRegistry; +import net.minecraft.data.worldgen.BiomeDecoratorGroups; +import net.minecraft.world.entity.EntityAreaEffectCloud; +import net.minecraft.world.entity.EntityExperienceOrb; +import net.minecraft.world.entity.EntityInsentient; +import net.minecraft.world.entity.EntityTypes; +import net.minecraft.world.entity.EnumMobSpawn; +import net.minecraft.world.entity.GroupDataEntity; +import net.minecraft.world.entity.decoration.EntityArmorStand; +import net.minecraft.world.entity.decoration.EntityHanging; +import net.minecraft.world.entity.decoration.EntityItemFrame; +import net.minecraft.world.entity.decoration.EntityLeash; +import net.minecraft.world.entity.decoration.EntityPainting; +import net.minecraft.world.entity.item.EntityFallingBlock; +import net.minecraft.world.entity.item.EntityTNTPrimed; +import net.minecraft.world.entity.monster.EntityZombie; +import net.minecraft.world.entity.projectile.EntityEgg; +import net.minecraft.world.entity.projectile.EntityEnderSignal; +import net.minecraft.world.entity.projectile.EntityEvokerFangs; +import net.minecraft.world.entity.projectile.EntityFireball; +import net.minecraft.world.entity.projectile.EntityFireworks; +import net.minecraft.world.entity.projectile.EntityPotion; +import net.minecraft.world.entity.projectile.EntitySnowball; +import net.minecraft.world.entity.projectile.EntityTippedArrow; +import net.minecraft.world.entity.vehicle.EntityBoat; +import net.minecraft.world.entity.vehicle.EntityMinecartChest; +import net.minecraft.world.entity.vehicle.EntityMinecartCommandBlock; +import net.minecraft.world.entity.vehicle.EntityMinecartFurnace; +import net.minecraft.world.entity.vehicle.EntityMinecartHopper; +import net.minecraft.world.entity.vehicle.EntityMinecartMobSpawner; +import net.minecraft.world.entity.vehicle.EntityMinecartRideable; +import net.minecraft.world.entity.vehicle.EntityMinecartTNT; +import net.minecraft.world.level.GeneratorAccessSeed; +import net.minecraft.world.level.biome.BiomeBase; +import net.minecraft.world.level.block.BlockChorusFlower; +import net.minecraft.world.level.block.BlockDiodeAbstract; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.IBlockData; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext; +import net.minecraft.world.phys.AxisAlignedBB; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.RegionAccessor; +import org.bukkit.TreeType; +import org.bukkit.block.Biome; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.potion.CraftPotionUtil; +import org.bukkit.craftbukkit.util.BlockStateListPopulator; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.entity.AbstractArrow; +import org.bukkit.entity.AbstractHorse; +import org.bukkit.entity.AbstractSkeleton; +import org.bukkit.entity.AbstractVillager; +import org.bukkit.entity.Ambient; +import org.bukkit.entity.AreaEffectCloud; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Axolotl; +import org.bukkit.entity.Bat; +import org.bukkit.entity.Bee; +import org.bukkit.entity.Blaze; +import org.bukkit.entity.Boat; +import org.bukkit.entity.Cat; +import org.bukkit.entity.CaveSpider; +import org.bukkit.entity.ChestedHorse; +import org.bukkit.entity.Chicken; +import org.bukkit.entity.Cod; +import org.bukkit.entity.ComplexLivingEntity; +import org.bukkit.entity.Cow; +import org.bukkit.entity.Creeper; +import org.bukkit.entity.Dolphin; +import org.bukkit.entity.Donkey; +import org.bukkit.entity.DragonFireball; +import org.bukkit.entity.Drowned; +import org.bukkit.entity.Egg; +import org.bukkit.entity.ElderGuardian; +import org.bukkit.entity.EnderCrystal; +import org.bukkit.entity.EnderDragon; +import org.bukkit.entity.EnderPearl; +import org.bukkit.entity.EnderSignal; +import org.bukkit.entity.Enderman; +import org.bukkit.entity.Endermite; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Evoker; +import org.bukkit.entity.EvokerFangs; +import org.bukkit.entity.ExperienceOrb; +import org.bukkit.entity.FallingBlock; +import org.bukkit.entity.Fireball; +import org.bukkit.entity.Firework; +import org.bukkit.entity.Fish; +import org.bukkit.entity.Fox; +import org.bukkit.entity.Ghast; +import org.bukkit.entity.Giant; +import org.bukkit.entity.GlowItemFrame; +import org.bukkit.entity.GlowSquid; +import org.bukkit.entity.Goat; +import org.bukkit.entity.Golem; +import org.bukkit.entity.Guardian; +import org.bukkit.entity.Hanging; +import org.bukkit.entity.Hoglin; +import org.bukkit.entity.Husk; +import org.bukkit.entity.Illager; +import org.bukkit.entity.Illusioner; +import org.bukkit.entity.IronGolem; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.LeashHitch; +import org.bukkit.entity.LightningStrike; +import org.bukkit.entity.LingeringPotion; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Llama; +import org.bukkit.entity.LlamaSpit; +import org.bukkit.entity.MagmaCube; +import org.bukkit.entity.Marker; +import org.bukkit.entity.Minecart; +import org.bukkit.entity.Mule; +import org.bukkit.entity.MushroomCow; +import org.bukkit.entity.Ocelot; +import org.bukkit.entity.Painting; +import org.bukkit.entity.Panda; +import org.bukkit.entity.Parrot; +import org.bukkit.entity.Phantom; +import org.bukkit.entity.Pig; +import org.bukkit.entity.PigZombie; +import org.bukkit.entity.Piglin; +import org.bukkit.entity.PiglinBrute; +import org.bukkit.entity.Pillager; +import org.bukkit.entity.Player; +import org.bukkit.entity.PolarBear; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.PufferFish; +import org.bukkit.entity.Rabbit; +import org.bukkit.entity.Ravager; +import org.bukkit.entity.Salmon; +import org.bukkit.entity.Sheep; +import org.bukkit.entity.Shulker; +import org.bukkit.entity.ShulkerBullet; +import org.bukkit.entity.Silverfish; +import org.bukkit.entity.Skeleton; +import org.bukkit.entity.SkeletonHorse; +import org.bukkit.entity.Slime; +import org.bukkit.entity.SmallFireball; +import org.bukkit.entity.Snowball; +import org.bukkit.entity.Snowman; +import org.bukkit.entity.SpectralArrow; +import org.bukkit.entity.Spellcaster; +import org.bukkit.entity.Spider; +import org.bukkit.entity.Squid; +import org.bukkit.entity.Stray; +import org.bukkit.entity.Strider; +import org.bukkit.entity.TNTPrimed; +import org.bukkit.entity.Tameable; +import org.bukkit.entity.ThrownExpBottle; +import org.bukkit.entity.ThrownPotion; +import org.bukkit.entity.TippedArrow; +import org.bukkit.entity.TraderLlama; +import org.bukkit.entity.Trident; +import org.bukkit.entity.TropicalFish; +import org.bukkit.entity.Turtle; +import org.bukkit.entity.Vex; +import org.bukkit.entity.Villager; +import org.bukkit.entity.Vindicator; +import org.bukkit.entity.WanderingTrader; +import org.bukkit.entity.Witch; +import org.bukkit.entity.Wither; +import org.bukkit.entity.WitherSkeleton; +import org.bukkit.entity.WitherSkull; +import org.bukkit.entity.Wolf; +import org.bukkit.entity.Zoglin; +import org.bukkit.entity.Zombie; +import org.bukkit.entity.ZombieHorse; +import org.bukkit.entity.ZombieVillager; +import org.bukkit.entity.minecart.CommandMinecart; +import org.bukkit.entity.minecart.ExplosiveMinecart; +import org.bukkit.entity.minecart.HopperMinecart; +import org.bukkit.entity.minecart.PoweredMinecart; +import org.bukkit.entity.minecart.SpawnerMinecart; +import org.bukkit.entity.minecart.StorageMinecart; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionData; +import org.bukkit.potion.PotionType; +import org.bukkit.util.Consumer; +import org.bukkit.util.Vector; + +public abstract class CraftRegionAccessor implements RegionAccessor { + + public abstract GeneratorAccessSeed getHandle(); + + public boolean isNormalWorld() { + return getHandle() instanceof net.minecraft.server.level.WorldServer; + } + + @Override + public Biome getBiome(Location location) { + return getBiome(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + } + + @Override + public Biome getBiome(int x, int y, int z) { + return CraftBlock.biomeBaseToBiome(getHandle().t().d(IRegistry.BIOME_REGISTRY), getHandle().getBiome(x >> 2, y >> 2, z >> 2)); + } + + @Override + public void setBiome(Location location, Biome biome) { + setBiome(location.getBlockX(), location.getBlockY(), location.getBlockZ(), biome); + } + + @Override + public void setBiome(int x, int y, int z, Biome biome) { + Preconditions.checkArgument(biome != Biome.CUSTOM, "Cannot set the biome to %s", biome); + BiomeBase biomeBase = CraftBlock.biomeToBiomeBase(getHandle().t().d(IRegistry.BIOME_REGISTRY), biome); + setBiome(x, y, z, biomeBase); + } + + public abstract void setBiome(int x, int y, int z, BiomeBase biomeBase); + + @Override + public BlockState getBlockState(Location location) { + return getBlockState(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + } + + @Override + public BlockState getBlockState(int x, int y, int z) { + return CraftBlock.at(getHandle(), new BlockPosition(x, y, z)).getState(); + } + + @Override + public BlockData getBlockData(Location location) { + return getBlockData(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + } + + @Override + public BlockData getBlockData(int x, int y, int z) { + return CraftBlockData.fromData(getData(x, y, z)); + } + + @Override + public Material getType(Location location) { + return getType(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + } + + @Override + public Material getType(int x, int y, int z) { + return CraftMagicNumbers.getMaterial(getData(x, y, z).getBlock()); + } + + private IBlockData getData(int x, int y, int z) { + return getHandle().getType(new BlockPosition(x, y, z)); + } + + @Override + public void setBlockData(Location location, BlockData blockData) { + setBlockData(location.getBlockX(), location.getBlockY(), location.getBlockZ(), blockData); + } + + @Override + public void setBlockData(int x, int y, int z, BlockData blockData) { + getHandle().setTypeAndData(new BlockPosition(x, y, z), ((CraftBlockData) blockData).getState(), 3); + } + + @Override + public void setType(Location location, Material material) { + setType(location.getBlockX(), location.getBlockY(), location.getBlockZ(), material); + } + + @Override + public void setType(int x, int y, int z, Material material) { + setBlockData(x, y, z, material.createBlockData()); + } + + @Override + public boolean generateTree(Location location, Random random, TreeType treeType) { + BlockPosition pos = new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + return generateTree(getHandle(), getHandle().getMinecraftWorld().getChunkProvider().generator, pos, random, treeType); + } + + @Override + public boolean generateTree(Location location, Random random, TreeType treeType, Consumer consumer) { + BlockPosition pos = new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + BlockStateListPopulator populator = new BlockStateListPopulator(getHandle()); + boolean result = generateTree(populator, getHandle().getMinecraftWorld().getChunkProvider().generator, pos, random, treeType); + + for (BlockState blockState : populator.getList()) { + if (consumer != null) { + consumer.accept(blockState); + } + blockState.update(true, true); + } + + return result; + } + + public boolean generateTree(GeneratorAccessSeed access, ChunkGenerator chunkGenerator, BlockPosition pos, Random random, TreeType treeType) { + net.minecraft.world.level.levelgen.feature.WorldGenFeatureConfigured gen; + switch (treeType) { + case BIG_TREE: + gen = BiomeDecoratorGroups.FANCY_OAK; + break; + case BIRCH: + gen = BiomeDecoratorGroups.BIRCH; + break; + case REDWOOD: + gen = BiomeDecoratorGroups.SPRUCE; + break; + case TALL_REDWOOD: + gen = BiomeDecoratorGroups.PINE; + break; + case JUNGLE: + gen = BiomeDecoratorGroups.MEGA_JUNGLE_TREE; + break; + case SMALL_JUNGLE: + gen = BiomeDecoratorGroups.JUNGLE_TREE_NO_VINE; + break; + case COCOA_TREE: + gen = BiomeDecoratorGroups.JUNGLE_TREE; + break; + case JUNGLE_BUSH: + gen = BiomeDecoratorGroups.JUNGLE_BUSH; + break; + case RED_MUSHROOM: + gen = BiomeDecoratorGroups.HUGE_RED_MUSHROOM; + break; + case BROWN_MUSHROOM: + gen = BiomeDecoratorGroups.HUGE_BROWN_MUSHROOM; + break; + case SWAMP: + gen = BiomeDecoratorGroups.SWAMP_OAK; + break; + case ACACIA: + gen = BiomeDecoratorGroups.ACACIA; + break; + case DARK_OAK: + gen = BiomeDecoratorGroups.DARK_OAK; + break; + case MEGA_REDWOOD: + gen = BiomeDecoratorGroups.MEGA_PINE; + break; + case TALL_BIRCH: + gen = BiomeDecoratorGroups.SUPER_BIRCH_BEES_0002; + break; + case CHORUS_PLANT: + ((BlockChorusFlower) Blocks.CHORUS_FLOWER).a(access, pos, random, 8); + return true; + case CRIMSON_FUNGUS: + gen = BiomeDecoratorGroups.CRIMSON_FUNGI_PLANTED; + break; + case WARPED_FUNGUS: + gen = BiomeDecoratorGroups.WARPED_FUNGI_PLANTED; + break; + case AZALEA: + gen = BiomeDecoratorGroups.AZALEA_TREE; + break; + case TREE: + default: + gen = BiomeDecoratorGroups.OAK; + break; + } + + return gen.feature.generate(new FeaturePlaceContext(access, chunkGenerator, random, pos, gen.config)); + } + + @Override + public Entity spawnEntity(Location location, EntityType entityType) { + return spawn(location, entityType.getEntityClass()); + } + + @Override + public List getEntities() { + List list = new ArrayList(); + + getNMSEntities().forEach(entity -> { + Entity bukkitEntity = entity.getBukkitEntity(); + + // Assuming that bukkitEntity isn't null + if (bukkitEntity != null && (!isNormalWorld() || bukkitEntity.isValid())) { + list.add(bukkitEntity); + } + }); + + return list; + } + + @Override + public List getLivingEntities() { + List list = new ArrayList(); + + getNMSEntities().forEach(entity -> { + Entity bukkitEntity = entity.getBukkitEntity(); + + // Assuming that bukkitEntity isn't null + if (bukkitEntity != null && bukkitEntity instanceof LivingEntity && (!isNormalWorld() || bukkitEntity.isValid())) { + list.add((LivingEntity) bukkitEntity); + } + }); + + return list; + } + + @Override + @SuppressWarnings("unchecked") + public Collection getEntitiesByClass(Class clazz) { + Collection list = new ArrayList(); + + getNMSEntities().forEach(entity -> { + Entity bukkitEntity = entity.getBukkitEntity(); + + if (bukkitEntity == null) { + return; + } + + Class bukkitClass = bukkitEntity.getClass(); + + if (clazz.isAssignableFrom(bukkitClass) && (!isNormalWorld() || bukkitEntity.isValid())) { + list.add((T) bukkitEntity); + } + }); + + return list; + } + + @Override + public Collection getEntitiesByClasses(Class... classes) { + Collection list = new ArrayList(); + + getNMSEntities().forEach(entity -> { + Entity bukkitEntity = entity.getBukkitEntity(); + + if (bukkitEntity == null) { + return; + } + + Class bukkitClass = bukkitEntity.getClass(); + + for (Class clazz : classes) { + if (clazz.isAssignableFrom(bukkitClass)) { + if (!isNormalWorld() || bukkitEntity.isValid()) { + list.add(bukkitEntity); + } + break; + } + } + }); + + return list; + } + + public abstract Iterable getNMSEntities(); + + @Override + public T spawn(Location location, Class clazz) throws IllegalArgumentException { + return spawn(location, clazz, null, CreatureSpawnEvent.SpawnReason.CUSTOM); + } + + @Override + public T spawn(Location location, Class clazz, Consumer function) throws IllegalArgumentException { + return spawn(location, clazz, function, CreatureSpawnEvent.SpawnReason.CUSTOM); + } + + public T spawn(Location location, Class clazz, Consumer function, CreatureSpawnEvent.SpawnReason reason) throws IllegalArgumentException { + net.minecraft.world.entity.Entity entity = createEntity(location, clazz); + + return addEntity(entity, reason, function); + } + + @SuppressWarnings("unchecked") + public T addEntity(net.minecraft.world.entity.Entity entity, CreatureSpawnEvent.SpawnReason reason) throws IllegalArgumentException { + return addEntity(entity, reason, null); + } + + @SuppressWarnings("unchecked") + public T addEntity(net.minecraft.world.entity.Entity entity, CreatureSpawnEvent.SpawnReason reason, Consumer function) throws IllegalArgumentException { + Preconditions.checkArgument(entity != null, "Cannot spawn null entity"); + + if (entity instanceof EntityInsentient) { + ((EntityInsentient) entity).prepare(getHandle(), getHandle().getDamageScaler(entity.getChunkCoordinates()), EnumMobSpawn.COMMAND, (GroupDataEntity) null, null); + } + + if (!isNormalWorld()) { + entity.generation = true; + } + + if (function != null) { + function.accept((T) entity.getBukkitEntity()); + } + + addEntityToWorld(entity, reason); + return (T) entity.getBukkitEntity(); + } + + public abstract void addEntityToWorld(net.minecraft.world.entity.Entity entity, CreatureSpawnEvent.SpawnReason reason); + + @SuppressWarnings("unchecked") + public net.minecraft.world.entity.Entity createEntity(Location location, Class clazz) throws IllegalArgumentException { + if (location == null || clazz == null) { + throw new IllegalArgumentException("Location or entity class cannot be null"); + } + + net.minecraft.world.entity.Entity entity = null; + net.minecraft.world.level.World world = getHandle().getMinecraftWorld(); + + double x = location.getX(); + double y = location.getY(); + double z = location.getZ(); + float pitch = location.getPitch(); + float yaw = location.getYaw(); + + // order is important for some of these + if (Boat.class.isAssignableFrom(clazz)) { + entity = new EntityBoat(world, x, y, z); + entity.setPositionRotation(x, y, z, yaw, pitch); + } else if (FallingBlock.class.isAssignableFrom(clazz)) { + entity = new EntityFallingBlock(world, x, y, z, getHandle().getType(new BlockPosition(x, y, z))); + } else if (Projectile.class.isAssignableFrom(clazz)) { + if (Snowball.class.isAssignableFrom(clazz)) { + entity = new EntitySnowball(world, x, y, z); + } else if (Egg.class.isAssignableFrom(clazz)) { + entity = new EntityEgg(world, x, y, z); + } else if (AbstractArrow.class.isAssignableFrom(clazz)) { + if (TippedArrow.class.isAssignableFrom(clazz)) { + entity = EntityTypes.ARROW.a(world); + ((EntityTippedArrow) entity).setType(CraftPotionUtil.fromBukkit(new PotionData(PotionType.WATER, false, false))); + } else if (SpectralArrow.class.isAssignableFrom(clazz)) { + entity = EntityTypes.SPECTRAL_ARROW.a(world); + } else if (Trident.class.isAssignableFrom(clazz)) { + entity = EntityTypes.TRIDENT.a(world); + } else { + entity = EntityTypes.ARROW.a(world); + } + entity.setPositionRotation(x, y, z, 0, 0); + } else if (ThrownExpBottle.class.isAssignableFrom(clazz)) { + entity = EntityTypes.EXPERIENCE_BOTTLE.a(world); + entity.setPositionRotation(x, y, z, 0, 0); + } else if (EnderPearl.class.isAssignableFrom(clazz)) { + entity = EntityTypes.ENDER_PEARL.a(world); + entity.setPositionRotation(x, y, z, 0, 0); + } else if (ThrownPotion.class.isAssignableFrom(clazz)) { + if (LingeringPotion.class.isAssignableFrom(clazz)) { + entity = new EntityPotion(world, x, y, z); + ((EntityPotion) entity).setItem(CraftItemStack.asNMSCopy(new ItemStack(org.bukkit.Material.LINGERING_POTION, 1))); + } else { + entity = new EntityPotion(world, x, y, z); + ((EntityPotion) entity).setItem(CraftItemStack.asNMSCopy(new ItemStack(org.bukkit.Material.SPLASH_POTION, 1))); + } + } else if (Fireball.class.isAssignableFrom(clazz)) { + if (SmallFireball.class.isAssignableFrom(clazz)) { + entity = EntityTypes.SMALL_FIREBALL.a(world); + } else if (WitherSkull.class.isAssignableFrom(clazz)) { + entity = EntityTypes.WITHER_SKULL.a(world); + } else if (DragonFireball.class.isAssignableFrom(clazz)) { + entity = EntityTypes.DRAGON_FIREBALL.a(world); + } else { + entity = EntityTypes.FIREBALL.a(world); + } + entity.setPositionRotation(x, y, z, yaw, pitch); + Vector direction = location.getDirection().multiply(10); + ((EntityFireball) entity).setDirection(direction.getX(), direction.getY(), direction.getZ()); + } else if (ShulkerBullet.class.isAssignableFrom(clazz)) { + entity = EntityTypes.SHULKER_BULLET.a(world); + entity.setPositionRotation(x, y, z, yaw, pitch); + } else if (LlamaSpit.class.isAssignableFrom(clazz)) { + entity = EntityTypes.LLAMA_SPIT.a(world); + entity.setPositionRotation(x, y, z, yaw, pitch); + } else if (Firework.class.isAssignableFrom(clazz)) { + entity = new EntityFireworks(world, x, y, z, net.minecraft.world.item.ItemStack.EMPTY); + } + } else if (Minecart.class.isAssignableFrom(clazz)) { + if (PoweredMinecart.class.isAssignableFrom(clazz)) { + entity = new EntityMinecartFurnace(world, x, y, z); + } else if (StorageMinecart.class.isAssignableFrom(clazz)) { + entity = new EntityMinecartChest(world, x, y, z); + } else if (ExplosiveMinecart.class.isAssignableFrom(clazz)) { + entity = new EntityMinecartTNT(world, x, y, z); + } else if (HopperMinecart.class.isAssignableFrom(clazz)) { + entity = new EntityMinecartHopper(world, x, y, z); + } else if (SpawnerMinecart.class.isAssignableFrom(clazz)) { + entity = new EntityMinecartMobSpawner(world, x, y, z); + } else if (CommandMinecart.class.isAssignableFrom(clazz)) { + entity = new EntityMinecartCommandBlock(world, x, y, z); + } else { // Default to rideable minecart for pre-rideable compatibility + entity = new EntityMinecartRideable(world, x, y, z); + } + } else if (EnderSignal.class.isAssignableFrom(clazz)) { + entity = new EntityEnderSignal(world, x, y, z); + } else if (EnderCrystal.class.isAssignableFrom(clazz)) { + entity = EntityTypes.END_CRYSTAL.a(world); + entity.setPositionRotation(x, y, z, 0, 0); + } else if (LivingEntity.class.isAssignableFrom(clazz)) { + if (Chicken.class.isAssignableFrom(clazz)) { + entity = EntityTypes.CHICKEN.a(world); + } else if (Cow.class.isAssignableFrom(clazz)) { + if (MushroomCow.class.isAssignableFrom(clazz)) { + entity = EntityTypes.MOOSHROOM.a(world); + } else { + entity = EntityTypes.COW.a(world); + } + } else if (Golem.class.isAssignableFrom(clazz)) { + if (Snowman.class.isAssignableFrom(clazz)) { + entity = EntityTypes.SNOW_GOLEM.a(world); + } else if (IronGolem.class.isAssignableFrom(clazz)) { + entity = EntityTypes.IRON_GOLEM.a(world); + } else if (Shulker.class.isAssignableFrom(clazz)) { + entity = EntityTypes.SHULKER.a(world); + } + } else if (Creeper.class.isAssignableFrom(clazz)) { + entity = EntityTypes.CREEPER.a(world); + } else if (Ghast.class.isAssignableFrom(clazz)) { + entity = EntityTypes.GHAST.a(world); + } else if (Pig.class.isAssignableFrom(clazz)) { + entity = EntityTypes.PIG.a(world); + } else if (Player.class.isAssignableFrom(clazz)) { + // need a net server handler for this one + } else if (Sheep.class.isAssignableFrom(clazz)) { + entity = EntityTypes.SHEEP.a(world); + } else if (AbstractHorse.class.isAssignableFrom(clazz)) { + if (ChestedHorse.class.isAssignableFrom(clazz)) { + if (Donkey.class.isAssignableFrom(clazz)) { + entity = EntityTypes.DONKEY.a(world); + } else if (Mule.class.isAssignableFrom(clazz)) { + entity = EntityTypes.MULE.a(world); + } else if (Llama.class.isAssignableFrom(clazz)) { + if (TraderLlama.class.isAssignableFrom(clazz)) { + entity = EntityTypes.TRADER_LLAMA.a(world); + } else { + entity = EntityTypes.LLAMA.a(world); + } + } + } else if (SkeletonHorse.class.isAssignableFrom(clazz)) { + entity = EntityTypes.SKELETON_HORSE.a(world); + } else if (ZombieHorse.class.isAssignableFrom(clazz)) { + entity = EntityTypes.ZOMBIE_HORSE.a(world); + } else { + entity = EntityTypes.HORSE.a(world); + } + } else if (AbstractSkeleton.class.isAssignableFrom(clazz)) { + if (Stray.class.isAssignableFrom(clazz)) { + entity = EntityTypes.STRAY.a(world); + } else if (WitherSkeleton.class.isAssignableFrom(clazz)) { + entity = EntityTypes.WITHER_SKELETON.a(world); + } else if (Skeleton.class.isAssignableFrom(clazz)) { + entity = EntityTypes.SKELETON.a(world); + } + } else if (Slime.class.isAssignableFrom(clazz)) { + if (MagmaCube.class.isAssignableFrom(clazz)) { + entity = EntityTypes.MAGMA_CUBE.a(world); + } else { + entity = EntityTypes.SLIME.a(world); + } + } else if (Spider.class.isAssignableFrom(clazz)) { + if (CaveSpider.class.isAssignableFrom(clazz)) { + entity = EntityTypes.CAVE_SPIDER.a(world); + } else { + entity = EntityTypes.SPIDER.a(world); + } + } else if (Squid.class.isAssignableFrom(clazz)) { + if (GlowSquid.class.isAssignableFrom(clazz)) { + entity = EntityTypes.GLOW_SQUID.a(world); + } else { + entity = EntityTypes.SQUID.a(world); + } + } else if (Tameable.class.isAssignableFrom(clazz)) { + if (Wolf.class.isAssignableFrom(clazz)) { + entity = EntityTypes.WOLF.a(world); + } else if (Parrot.class.isAssignableFrom(clazz)) { + entity = EntityTypes.PARROT.a(world); + } else if (Cat.class.isAssignableFrom(clazz)) { + entity = EntityTypes.CAT.a(world); + } + } else if (PigZombie.class.isAssignableFrom(clazz)) { + entity = EntityTypes.ZOMBIFIED_PIGLIN.a(world); + } else if (Zombie.class.isAssignableFrom(clazz)) { + if (Husk.class.isAssignableFrom(clazz)) { + entity = EntityTypes.HUSK.a(world); + } else if (ZombieVillager.class.isAssignableFrom(clazz)) { + entity = EntityTypes.ZOMBIE_VILLAGER.a(world); + } else if (Drowned.class.isAssignableFrom(clazz)) { + entity = EntityTypes.DROWNED.a(world); + } else { + entity = new EntityZombie(world); + } + } else if (Giant.class.isAssignableFrom(clazz)) { + entity = EntityTypes.GIANT.a(world); + } else if (Silverfish.class.isAssignableFrom(clazz)) { + entity = EntityTypes.SILVERFISH.a(world); + } else if (Enderman.class.isAssignableFrom(clazz)) { + entity = EntityTypes.ENDERMAN.a(world); + } else if (Blaze.class.isAssignableFrom(clazz)) { + entity = EntityTypes.BLAZE.a(world); + } else if (AbstractVillager.class.isAssignableFrom(clazz)) { + if (Villager.class.isAssignableFrom(clazz)) { + entity = EntityTypes.VILLAGER.a(world); + } else if (WanderingTrader.class.isAssignableFrom(clazz)) { + entity = EntityTypes.WANDERING_TRADER.a(world); + } + } else if (Witch.class.isAssignableFrom(clazz)) { + entity = EntityTypes.WITCH.a(world); + } else if (Wither.class.isAssignableFrom(clazz)) { + entity = EntityTypes.WITHER.a(world); + } else if (ComplexLivingEntity.class.isAssignableFrom(clazz)) { + if (EnderDragon.class.isAssignableFrom(clazz)) { + if (isNormalWorld()) { + entity = EntityTypes.ENDER_DRAGON.a(getHandle().getMinecraftWorld()); + } else { + throw new IllegalArgumentException("Cannot spawn entity " + clazz.getName() + " during world generation"); + } + } + } else if (Ambient.class.isAssignableFrom(clazz)) { + if (Bat.class.isAssignableFrom(clazz)) { + entity = EntityTypes.BAT.a(world); + } + } else if (Rabbit.class.isAssignableFrom(clazz)) { + entity = EntityTypes.RABBIT.a(world); + } else if (Endermite.class.isAssignableFrom(clazz)) { + entity = EntityTypes.ENDERMITE.a(world); + } else if (Guardian.class.isAssignableFrom(clazz)) { + if (ElderGuardian.class.isAssignableFrom(clazz)) { + entity = EntityTypes.ELDER_GUARDIAN.a(world); + } else { + entity = EntityTypes.GUARDIAN.a(world); + } + } else if (ArmorStand.class.isAssignableFrom(clazz)) { + entity = new EntityArmorStand(world, x, y, z); + } else if (PolarBear.class.isAssignableFrom(clazz)) { + entity = EntityTypes.POLAR_BEAR.a(world); + } else if (Vex.class.isAssignableFrom(clazz)) { + entity = EntityTypes.VEX.a(world); + } else if (Illager.class.isAssignableFrom(clazz)) { + if (Spellcaster.class.isAssignableFrom(clazz)) { + if (Evoker.class.isAssignableFrom(clazz)) { + entity = EntityTypes.EVOKER.a(world); + } else if (Illusioner.class.isAssignableFrom(clazz)) { + entity = EntityTypes.ILLUSIONER.a(world); + } + } else if (Vindicator.class.isAssignableFrom(clazz)) { + entity = EntityTypes.VINDICATOR.a(world); + } else if (Pillager.class.isAssignableFrom(clazz)) { + entity = EntityTypes.PILLAGER.a(world); + } + } else if (Turtle.class.isAssignableFrom(clazz)) { + entity = EntityTypes.TURTLE.a(world); + } else if (Phantom.class.isAssignableFrom(clazz)) { + entity = EntityTypes.PHANTOM.a(world); + } else if (Fish.class.isAssignableFrom(clazz)) { + if (Cod.class.isAssignableFrom(clazz)) { + entity = EntityTypes.COD.a(world); + } else if (PufferFish.class.isAssignableFrom(clazz)) { + entity = EntityTypes.PUFFERFISH.a(world); + } else if (Salmon.class.isAssignableFrom(clazz)) { + entity = EntityTypes.SALMON.a(world); + } else if (TropicalFish.class.isAssignableFrom(clazz)) { + entity = EntityTypes.TROPICAL_FISH.a(world); + } + } else if (Dolphin.class.isAssignableFrom(clazz)) { + entity = EntityTypes.DOLPHIN.a(world); + } else if (Ocelot.class.isAssignableFrom(clazz)) { + entity = EntityTypes.OCELOT.a(world); + } else if (Ravager.class.isAssignableFrom(clazz)) { + entity = EntityTypes.RAVAGER.a(world); + } else if (Panda.class.isAssignableFrom(clazz)) { + entity = EntityTypes.PANDA.a(world); + } else if (Fox.class.isAssignableFrom(clazz)) { + entity = EntityTypes.FOX.a(world); + } else if (Bee.class.isAssignableFrom(clazz)) { + entity = EntityTypes.BEE.a(world); + } else if (Hoglin.class.isAssignableFrom(clazz)) { + entity = EntityTypes.HOGLIN.a(world); + } else if (Piglin.class.isAssignableFrom(clazz)) { + entity = EntityTypes.PIGLIN.a(world); + } else if (PiglinBrute.class.isAssignableFrom(clazz)) { + entity = EntityTypes.PIGLIN_BRUTE.a(world); + } else if (Strider.class.isAssignableFrom(clazz)) { + entity = EntityTypes.STRIDER.a(world); + } else if (Zoglin.class.isAssignableFrom(clazz)) { + entity = EntityTypes.ZOGLIN.a(world); + } else if (Axolotl.class.isAssignableFrom(clazz)) { + entity = EntityTypes.AXOLOTL.a(world); + } else if (Goat.class.isAssignableFrom(clazz)) { + entity = EntityTypes.GOAT.a(world); + } + + if (entity != null) { + entity.setLocation(x, y, z, yaw, pitch); + entity.setHeadRotation(yaw); // SPIGOT-3587 + } + } else if (Hanging.class.isAssignableFrom(clazz)) { + BlockFace face = BlockFace.SELF; + + int width = 16; // 1 full block, also painting smallest size. + int height = 16; // 1 full block, also painting smallest size. + + if (ItemFrame.class.isAssignableFrom(clazz)) { + width = 12; + height = 12; + } else if (LeashHitch.class.isAssignableFrom(clazz)) { + width = 9; + height = 9; + } + + BlockFace[] faces = new BlockFace[]{BlockFace.EAST, BlockFace.NORTH, BlockFace.WEST, BlockFace.SOUTH, BlockFace.UP, BlockFace.DOWN}; + final BlockPosition pos = new BlockPosition(x, y, z); + for (BlockFace dir : faces) { + IBlockData nmsBlock = getHandle().getType(pos.shift(CraftBlock.blockFaceToNotch(dir))); + if (nmsBlock.getMaterial().isBuildable() || BlockDiodeAbstract.isDiode(nmsBlock)) { + boolean taken = false; + AxisAlignedBB bb = (ItemFrame.class.isAssignableFrom(clazz)) + ? EntityItemFrame.calculateBoundingBox(null, pos, CraftBlock.blockFaceToNotch(dir).opposite(), width, height) + : EntityHanging.calculateBoundingBox(null, pos, CraftBlock.blockFaceToNotch(dir).opposite(), width, height); + List list = (List) getHandle().getEntities(null, bb); + for (Iterator it = list.iterator(); !taken && it.hasNext();) { + net.minecraft.world.entity.Entity e = it.next(); + if (e instanceof EntityHanging) { + taken = true; // Hanging entities do not like hanging entities which intersect them. + } + } + + if (!taken) { + face = dir; + break; + } + } + } + + if (LeashHitch.class.isAssignableFrom(clazz)) { + entity = new EntityLeash(world, new BlockPosition(x, y, z)); + } else { + // No valid face found + Preconditions.checkArgument(face != BlockFace.SELF, "Cannot spawn hanging entity for %s at %s (no free face)", clazz.getName(), location); + + EnumDirection dir = CraftBlock.blockFaceToNotch(face).opposite(); + if (Painting.class.isAssignableFrom(clazz)) { + if (isNormalWorld()) { + entity = new EntityPainting(getHandle().getMinecraftWorld(), new BlockPosition(x, y, z), dir); + } else { + entity = new EntityPainting(EntityTypes.PAINTING, getHandle().getMinecraftWorld()); + entity.setLocation(x, y, z, yaw, pitch); + ((EntityPainting) entity).setDirection(dir); + } + } else if (ItemFrame.class.isAssignableFrom(clazz)) { + if (GlowItemFrame.class.isAssignableFrom(clazz)) { + entity = new net.minecraft.world.entity.decoration.GlowItemFrame(world, new BlockPosition(x, y, z), dir); + } else { + entity = new EntityItemFrame(world, new BlockPosition(x, y, z), dir); + } + } + } + + // survives call does not work during world generation + if (entity != null && isNormalWorld() && !((EntityHanging) entity).survives()) { + throw new IllegalArgumentException("Cannot spawn hanging entity for " + clazz.getName() + " at " + location); + } + } else if (TNTPrimed.class.isAssignableFrom(clazz)) { + entity = new EntityTNTPrimed(world, x, y, z, null); + } else if (ExperienceOrb.class.isAssignableFrom(clazz)) { + entity = new EntityExperienceOrb(world, x, y, z, 0); + } else if (LightningStrike.class.isAssignableFrom(clazz)) { + entity = EntityTypes.LIGHTNING_BOLT.a(world); + entity.teleportAndSync(location.getX(), location.getY(), location.getZ()); + } else if (AreaEffectCloud.class.isAssignableFrom(clazz)) { + entity = new EntityAreaEffectCloud(world, x, y, z); + } else if (EvokerFangs.class.isAssignableFrom(clazz)) { + entity = new EntityEvokerFangs(world, x, y, z, (float) Math.toRadians(yaw), 0, null); + } else if (Marker.class.isAssignableFrom(clazz)) { + entity = EntityTypes.MARKER.a(world); + entity.setPosition(x, y, z); + } + + if (entity != null) { + return entity; + } + + throw new IllegalArgumentException("Cannot spawn an entity for " + clazz.getName()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java index 3b0e63426..803873175 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -100,9 +100,11 @@ import net.minecraft.world.level.GameRules; import net.minecraft.world.level.MobSpawner; import net.minecraft.world.level.WorldSettings; import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.biome.WorldChunkManager; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.dimension.DimensionManager; import net.minecraft.world.level.dimension.WorldDimension; +import net.minecraft.world.level.levelgen.ChunkGeneratorAbstract; import net.minecraft.world.level.levelgen.GeneratorSettings; import net.minecraft.world.level.levelgen.MobSpawnerPatrol; import net.minecraft.world.level.levelgen.MobSpawnerPhantom; @@ -154,7 +156,9 @@ import org.bukkit.craftbukkit.command.CraftCommandMap; import org.bukkit.craftbukkit.command.VanillaCommandWrapper; import org.bukkit.craftbukkit.entity.CraftPlayer; import org.bukkit.craftbukkit.event.CraftEventFactory; -import org.bukkit.craftbukkit.generator.CraftChunkData; +import org.bukkit.craftbukkit.generator.CraftWorldInfo; +import org.bukkit.craftbukkit.generator.CustomWorldChunkManager; +import org.bukkit.craftbukkit.generator.OldCraftChunkData; import org.bukkit.craftbukkit.help.SimpleHelpMap; import org.bukkit.craftbukkit.inventory.CraftBlastingRecipe; import org.bukkit.craftbukkit.inventory.CraftCampfireRecipe; @@ -196,7 +200,9 @@ import org.bukkit.event.server.ServerLoadEvent; import org.bukkit.event.server.TabCompleteEvent; import org.bukkit.event.world.WorldLoadEvent; import org.bukkit.event.world.WorldUnloadEvent; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.ChunkGenerator; +import org.bukkit.generator.WorldInfo; import org.bukkit.help.HelpMap; import org.bukkit.inventory.BlastingRecipe; import org.bukkit.inventory.CampfireRecipe; @@ -956,6 +962,7 @@ public final class CraftServer implements Server { String name = creator.name(); ChunkGenerator generator = creator.generator(); + BiomeProvider biomeProvider = creator.biomeProvider(); File folder = new File(getWorldContainer(), name); World world = getWorld(name); @@ -971,6 +978,10 @@ public final class CraftServer implements Server { generator = getGenerator(name); } + if (biomeProvider == null) { + biomeProvider = getBiomeProvider(name); + } + ResourceKey actualDimension; switch (creator.environment()) { case NORMAL: @@ -1038,6 +1049,18 @@ public final class CraftServer implements Server { chunkgenerator = worlddimension.c(); } + WorldInfo worldInfo = new CraftWorldInfo(worlddata, worldSession, creator.environment(), dimensionmanager); + if (biomeProvider == null && generator != null) { + biomeProvider = generator.getDefaultBiomeProvider(worldInfo); + } + + if (biomeProvider != null) { + WorldChunkManager worldChunkManager = new CustomWorldChunkManager(worldInfo, biomeProvider, console.registryHolder.b(IRegistry.BIOME_REGISTRY)); + if (chunkgenerator instanceof ChunkGeneratorAbstract) { + chunkgenerator = new ChunkGeneratorAbstract(worldChunkManager, chunkgenerator.strongholdSeed, ((ChunkGeneratorAbstract) chunkgenerator).settings); + } + } + ResourceKey worldKey; String levelName = this.getServer().getDedicatedServerProperties().levelName; if (name.equals(levelName + "_nether")) { @@ -1049,7 +1072,7 @@ public final class CraftServer implements Server { } WorldServer internal = (WorldServer) new WorldServer(console, console.executor, worldSession, worlddata, worldKey, dimensionmanager, getServer().progressListenerFactory.create(11), - chunkgenerator, worlddata.getGeneratorSettings().isDebugWorld(), j, creator.environment() == Environment.NORMAL ? list : ImmutableList.of(), true, creator.environment(), generator); + chunkgenerator, worlddata.getGeneratorSettings().isDebugWorld(), j, creator.environment() == Environment.NORMAL ? list : ImmutableList.of(), true, creator.environment(), generator, biomeProvider); if (!(worlds.containsKey(name.toLowerCase(java.util.Locale.ENGLISH)))) { return null; @@ -1424,6 +1447,42 @@ public final class CraftServer implements Server { return result; } + public BiomeProvider getBiomeProvider(String world) { + ConfigurationSection section = configuration.getConfigurationSection("worlds"); + BiomeProvider result = null; + + if (section != null) { + section = section.getConfigurationSection(world); + + if (section != null) { + String name = section.getString("biome-provider"); + + if ((name != null) && (!name.equals(""))) { + String[] split = name.split(":", 2); + String id = (split.length > 1) ? split[1] : null; + Plugin plugin = pluginManager.getPlugin(split[0]); + + if (plugin == null) { + getLogger().severe("Could not set biome provider for default world '" + world + "': Plugin '" + split[0] + "' does not exist"); + } else if (!plugin.isEnabled()) { + getLogger().severe("Could not set biome provider for default world '" + world + "': Plugin '" + plugin.getDescription().getFullName() + "' is not enabled yet (is it load:STARTUP?)"); + } else { + try { + result = plugin.getDefaultBiomeProvider(world, id); + if (result == null) { + getLogger().severe("Could not set biome provider for default world '" + world + "': Plugin '" + plugin.getDescription().getFullName() + "' lacks a default world biome provider"); + } + } catch (Throwable t) { + plugin.getLogger().log(Level.SEVERE, "Could not set biome provider for default world '" + world + "': Plugin '" + plugin.getDescription().getFullName(), t); + } + } + } + } + } + + return result; + } + @Override @Deprecated public CraftMapView getMap(int id) { @@ -1919,7 +1978,7 @@ public final class CraftServer implements Server { @Override public ChunkGenerator.ChunkData createChunkData(World world) { Validate.notNull(world, "World cannot be null"); - return new CraftChunkData(world); + return new OldCraftChunkData(world); } @Override diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index 3dbb6e7a7..688df033e 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -13,7 +13,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -23,9 +22,6 @@ import java.util.UUID; import java.util.function.Predicate; import java.util.stream.Collectors; import net.minecraft.core.BlockPosition; -import net.minecraft.core.EnumDirection; -import net.minecraft.core.IRegistry; -import net.minecraft.data.worldgen.BiomeDecoratorGroups; import net.minecraft.network.protocol.game.PacketPlayOutCustomSoundEffect; import net.minecraft.network.protocol.game.PacketPlayOutUpdateTime; import net.minecraft.network.protocol.game.PacketPlayOutWorldEvent; @@ -39,54 +35,23 @@ import net.minecraft.sounds.SoundCategory; import net.minecraft.util.ArraySetSorted; import net.minecraft.util.Unit; import net.minecraft.world.EnumDifficulty; -import net.minecraft.world.entity.EntityAreaEffectCloud; -import net.minecraft.world.entity.EntityExperienceOrb; -import net.minecraft.world.entity.EntityInsentient; import net.minecraft.world.entity.EntityLightning; import net.minecraft.world.entity.EntityTypes; -import net.minecraft.world.entity.EnumMobSpawn; -import net.minecraft.world.entity.GroupDataEntity; -import net.minecraft.world.entity.decoration.EntityArmorStand; -import net.minecraft.world.entity.decoration.EntityHanging; -import net.minecraft.world.entity.decoration.EntityItemFrame; -import net.minecraft.world.entity.decoration.EntityLeash; -import net.minecraft.world.entity.decoration.EntityPainting; import net.minecraft.world.entity.item.EntityFallingBlock; import net.minecraft.world.entity.item.EntityItem; -import net.minecraft.world.entity.item.EntityTNTPrimed; -import net.minecraft.world.entity.monster.EntityZombie; import net.minecraft.world.entity.player.EntityHuman; import net.minecraft.world.entity.projectile.EntityArrow; -import net.minecraft.world.entity.projectile.EntityEgg; -import net.minecraft.world.entity.projectile.EntityEnderSignal; -import net.minecraft.world.entity.projectile.EntityEvokerFangs; -import net.minecraft.world.entity.projectile.EntityFireball; -import net.minecraft.world.entity.projectile.EntityFireworks; -import net.minecraft.world.entity.projectile.EntityPotion; -import net.minecraft.world.entity.projectile.EntitySnowball; import net.minecraft.world.entity.projectile.EntityTippedArrow; import net.minecraft.world.entity.raid.PersistentRaid; -import net.minecraft.world.entity.vehicle.EntityBoat; -import net.minecraft.world.entity.vehicle.EntityMinecartChest; -import net.minecraft.world.entity.vehicle.EntityMinecartCommandBlock; -import net.minecraft.world.entity.vehicle.EntityMinecartFurnace; -import net.minecraft.world.entity.vehicle.EntityMinecartHopper; -import net.minecraft.world.entity.vehicle.EntityMinecartMobSpawner; -import net.minecraft.world.entity.vehicle.EntityMinecartRideable; -import net.minecraft.world.entity.vehicle.EntityMinecartTNT; import net.minecraft.world.level.ChunkCoordIntPair; import net.minecraft.world.level.Explosion; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.RayTrace; import net.minecraft.world.level.biome.BiomeBase; -import net.minecraft.world.level.block.BlockChorusFlower; -import net.minecraft.world.level.block.BlockDiodeAbstract; import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.IBlockData; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.IChunkAccess; import net.minecraft.world.level.chunk.ProtoChunkExtension; -import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext; import net.minecraft.world.level.levelgen.feature.StructureGenerator; import net.minecraft.world.level.storage.SavedFile; import net.minecraft.world.phys.AxisAlignedBB; @@ -108,16 +73,13 @@ import org.bukkit.Sound; import org.bukkit.StructureType; import org.bukkit.TreeType; import org.bukkit.World; -import org.bukkit.World.Environment; import org.bukkit.WorldBorder; import org.bukkit.block.Biome; import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.block.data.BlockData; import org.bukkit.boss.DragonBattle; import org.bukkit.craftbukkit.block.CraftBlock; -import org.bukkit.craftbukkit.block.CraftBlockState; import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.craftbukkit.boss.CraftDragonBattle; import org.bukkit.craftbukkit.entity.CraftEntity; @@ -128,139 +90,20 @@ import org.bukkit.craftbukkit.potion.CraftPotionUtil; import org.bukkit.craftbukkit.util.CraftMagicNumbers; import org.bukkit.craftbukkit.util.CraftRayTraceResult; import org.bukkit.entity.AbstractArrow; -import org.bukkit.entity.AbstractHorse; -import org.bukkit.entity.AbstractSkeleton; -import org.bukkit.entity.AbstractVillager; -import org.bukkit.entity.Ambient; -import org.bukkit.entity.AreaEffectCloud; -import org.bukkit.entity.ArmorStand; import org.bukkit.entity.Arrow; -import org.bukkit.entity.Axolotl; -import org.bukkit.entity.Bat; -import org.bukkit.entity.Bee; -import org.bukkit.entity.Blaze; -import org.bukkit.entity.Boat; -import org.bukkit.entity.Cat; -import org.bukkit.entity.CaveSpider; -import org.bukkit.entity.ChestedHorse; -import org.bukkit.entity.Chicken; -import org.bukkit.entity.Cod; -import org.bukkit.entity.ComplexLivingEntity; -import org.bukkit.entity.Cow; -import org.bukkit.entity.Creeper; -import org.bukkit.entity.Dolphin; -import org.bukkit.entity.Donkey; -import org.bukkit.entity.DragonFireball; -import org.bukkit.entity.Drowned; -import org.bukkit.entity.Egg; -import org.bukkit.entity.ElderGuardian; -import org.bukkit.entity.EnderCrystal; -import org.bukkit.entity.EnderDragon; -import org.bukkit.entity.EnderPearl; -import org.bukkit.entity.EnderSignal; -import org.bukkit.entity.Enderman; -import org.bukkit.entity.Endermite; import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Evoker; -import org.bukkit.entity.EvokerFangs; -import org.bukkit.entity.ExperienceOrb; import org.bukkit.entity.FallingBlock; -import org.bukkit.entity.Fireball; -import org.bukkit.entity.Firework; -import org.bukkit.entity.Fish; -import org.bukkit.entity.Fox; -import org.bukkit.entity.Ghast; -import org.bukkit.entity.Giant; -import org.bukkit.entity.GlowItemFrame; -import org.bukkit.entity.GlowSquid; -import org.bukkit.entity.Goat; -import org.bukkit.entity.Golem; -import org.bukkit.entity.Guardian; -import org.bukkit.entity.Hanging; -import org.bukkit.entity.Hoglin; import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.Husk; -import org.bukkit.entity.Illager; -import org.bukkit.entity.Illusioner; -import org.bukkit.entity.IronGolem; -import org.bukkit.entity.ItemFrame; -import org.bukkit.entity.LeashHitch; import org.bukkit.entity.LightningStrike; -import org.bukkit.entity.LingeringPotion; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Llama; -import org.bukkit.entity.LlamaSpit; -import org.bukkit.entity.MagmaCube; -import org.bukkit.entity.Marker; -import org.bukkit.entity.Minecart; -import org.bukkit.entity.Mule; -import org.bukkit.entity.MushroomCow; -import org.bukkit.entity.Ocelot; -import org.bukkit.entity.Painting; -import org.bukkit.entity.Panda; -import org.bukkit.entity.Parrot; -import org.bukkit.entity.Phantom; -import org.bukkit.entity.Pig; -import org.bukkit.entity.PigZombie; -import org.bukkit.entity.Piglin; -import org.bukkit.entity.PiglinBrute; -import org.bukkit.entity.Pillager; import org.bukkit.entity.Player; -import org.bukkit.entity.PolarBear; -import org.bukkit.entity.Projectile; -import org.bukkit.entity.PufferFish; -import org.bukkit.entity.Rabbit; -import org.bukkit.entity.Ravager; -import org.bukkit.entity.Salmon; -import org.bukkit.entity.Sheep; -import org.bukkit.entity.Shulker; -import org.bukkit.entity.ShulkerBullet; -import org.bukkit.entity.Silverfish; -import org.bukkit.entity.Skeleton; -import org.bukkit.entity.SkeletonHorse; -import org.bukkit.entity.Slime; -import org.bukkit.entity.SmallFireball; -import org.bukkit.entity.Snowball; -import org.bukkit.entity.Snowman; import org.bukkit.entity.SpectralArrow; -import org.bukkit.entity.Spellcaster; -import org.bukkit.entity.Spider; -import org.bukkit.entity.Squid; -import org.bukkit.entity.Stray; -import org.bukkit.entity.Strider; -import org.bukkit.entity.TNTPrimed; -import org.bukkit.entity.Tameable; -import org.bukkit.entity.ThrownExpBottle; -import org.bukkit.entity.ThrownPotion; import org.bukkit.entity.TippedArrow; -import org.bukkit.entity.TraderLlama; import org.bukkit.entity.Trident; -import org.bukkit.entity.TropicalFish; -import org.bukkit.entity.Turtle; -import org.bukkit.entity.Vex; -import org.bukkit.entity.Villager; -import org.bukkit.entity.Vindicator; -import org.bukkit.entity.WanderingTrader; -import org.bukkit.entity.Witch; -import org.bukkit.entity.Wither; -import org.bukkit.entity.WitherSkeleton; -import org.bukkit.entity.WitherSkull; -import org.bukkit.entity.Wolf; -import org.bukkit.entity.Zoglin; -import org.bukkit.entity.Zombie; -import org.bukkit.entity.ZombieHorse; -import org.bukkit.entity.ZombieVillager; -import org.bukkit.entity.minecart.CommandMinecart; -import org.bukkit.entity.minecart.ExplosiveMinecart; -import org.bukkit.entity.minecart.HopperMinecart; -import org.bukkit.entity.minecart.PoweredMinecart; -import org.bukkit.entity.minecart.SpawnerMinecart; -import org.bukkit.entity.minecart.StorageMinecart; import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; import org.bukkit.event.weather.LightningStrikeEvent; import org.bukkit.event.world.SpawnChangeEvent; import org.bukkit.event.world.TimeSkipEvent; +import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.ChunkGenerator; import org.bukkit.inventory.ItemStack; @@ -275,7 +118,7 @@ import org.bukkit.util.Consumer; import org.bukkit.util.RayTraceResult; import org.bukkit.util.Vector; -public class CraftWorld implements World { +public class CraftWorld extends CraftRegionAccessor implements World { public static final int CUSTOM_DIMENSION_OFFSET = 10; private final WorldServer world; @@ -283,6 +126,7 @@ public class CraftWorld implements World { private Environment environment; private final CraftServer server = (CraftServer) Bukkit.getServer(); private final ChunkGenerator generator; + private final BiomeProvider biomeProvider; private final List populators = new ArrayList(); private final BlockMetadataStore blockMetadata = new BlockMetadataStore(this); private int monsterSpawn = -1; @@ -293,9 +137,10 @@ public class CraftWorld implements World { private static final Random rand = new Random(); - public CraftWorld(WorldServer world, ChunkGenerator gen, Environment env) { + public CraftWorld(WorldServer world, ChunkGenerator gen, BiomeProvider biomeProvider, Environment env) { this.world = world; this.generator = gen; + this.biomeProvider = biomeProvider; environment = env; } @@ -666,11 +511,6 @@ public class CraftWorld implements World { return (T) arrow.getBukkitEntity(); } - @Override - public Entity spawnEntity(Location loc, EntityType entityType) { - return spawn(loc, entityType.getEntityClass()); - } - @Override public LightningStrike strikeLightning(Location loc) { EntityLightning lightning = EntityTypes.LIGHTNING_BOLT.a(world); @@ -690,74 +530,7 @@ public class CraftWorld implements World { @Override public boolean generateTree(Location loc, TreeType type) { - BlockPosition pos = new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); - - net.minecraft.world.level.levelgen.feature.WorldGenFeatureConfigured gen; - switch (type) { - case BIG_TREE: - gen = BiomeDecoratorGroups.FANCY_OAK; - break; - case BIRCH: - gen = BiomeDecoratorGroups.BIRCH; - break; - case REDWOOD: - gen = BiomeDecoratorGroups.SPRUCE; - break; - case TALL_REDWOOD: - gen = BiomeDecoratorGroups.PINE; - break; - case JUNGLE: - gen = BiomeDecoratorGroups.MEGA_JUNGLE_TREE; - break; - case SMALL_JUNGLE: - gen = BiomeDecoratorGroups.JUNGLE_TREE_NO_VINE; - break; - case COCOA_TREE: - gen = BiomeDecoratorGroups.JUNGLE_TREE; - break; - case JUNGLE_BUSH: - gen = BiomeDecoratorGroups.JUNGLE_BUSH; - break; - case RED_MUSHROOM: - gen = BiomeDecoratorGroups.HUGE_RED_MUSHROOM; - break; - case BROWN_MUSHROOM: - gen = BiomeDecoratorGroups.HUGE_BROWN_MUSHROOM; - break; - case SWAMP: - gen = BiomeDecoratorGroups.SWAMP_OAK; - break; - case ACACIA: - gen = BiomeDecoratorGroups.ACACIA; - break; - case DARK_OAK: - gen = BiomeDecoratorGroups.DARK_OAK; - break; - case MEGA_REDWOOD: - gen = BiomeDecoratorGroups.MEGA_PINE; - break; - case TALL_BIRCH: - gen = BiomeDecoratorGroups.SUPER_BIRCH_BEES_0002; - break; - case CHORUS_PLANT: - ((BlockChorusFlower) Blocks.CHORUS_FLOWER).a(world, pos, rand, 8); - return true; - case CRIMSON_FUNGUS: - gen = BiomeDecoratorGroups.CRIMSON_FUNGI_PLANTED; - break; - case WARPED_FUNGUS: - gen = BiomeDecoratorGroups.WARPED_FUNGI_PLANTED; - break; - case AZALEA: - gen = BiomeDecoratorGroups.AZALEA_TREE; - break; - case TREE: - default: - gen = BiomeDecoratorGroups.OAK; - break; - } - - return gen.feature.generate(new FeaturePlaceContext(world, world.getChunkProvider().getChunkGenerator(), rand, pos, gen.config)); + return generateTree(loc, rand, type); } @Override @@ -904,6 +677,11 @@ public class CraftWorld implements World { return generator; } + @Override + public BiomeProvider getBiomeProvider() { + return biomeProvider; + } + @Override public List getPopulators() { return populators; @@ -945,11 +723,6 @@ public class CraftWorld implements World { return getBiome(x, 0, z); } - @Override - public Biome getBiome(int x, int y, int z) { - return CraftBlock.biomeBaseToBiome(getHandle().t().d(IRegistry.BIOME_REGISTRY), this.world.getBiome(x >> 2, y >> 2, z >> 2)); - } - @Override public void setBiome(int x, int z, Biome bio) { for (int y = 0; y < getMaxHeight(); y++) { @@ -958,9 +731,7 @@ public class CraftWorld implements World { } @Override - public void setBiome(int x, int y, int z, Biome bio) { - Preconditions.checkArgument(bio != Biome.CUSTOM, "Cannot set the biome to %s", bio); - BiomeBase bb = CraftBlock.biomeToBiomeBase(getHandle().t().d(IRegistry.BIOME_REGISTRY), bio); + public void setBiome(int x, int y, int z, BiomeBase bb) { BlockPosition pos = new BlockPosition(x, 0, z); if (this.world.isLoaded(pos)) { net.minecraft.world.level.chunk.Chunk chunk = this.world.getChunkAtWorldCoords(pos); @@ -994,38 +765,6 @@ public class CraftWorld implements World { return this.world.getBiome(x >> 2, y >> 2, z >> 2).getHumidity(); } - @Override - public List getEntities() { - List list = new ArrayList(); - - world.getEntities().a().forEach((mcEnt) -> { - Entity bukkitEntity = mcEnt.getBukkitEntity(); - - // Assuming that bukkitEntity isn't null - if (bukkitEntity != null && bukkitEntity.isValid()) { - list.add(bukkitEntity); - } - }); - - return list; - } - - @Override - public List getLivingEntities() { - List list = new ArrayList(); - - world.getEntities().a().forEach((mcEnt) -> { - Entity bukkitEntity = mcEnt.getBukkitEntity(); - - // Assuming that bukkitEntity isn't null - if (bukkitEntity != null && bukkitEntity instanceof LivingEntity && bukkitEntity.isValid()) { - list.add((LivingEntity) bukkitEntity); - } - }); - - return list; - } - @Override @SuppressWarnings("unchecked") @Deprecated @@ -1034,51 +773,13 @@ public class CraftWorld implements World { } @Override - @SuppressWarnings("unchecked") - public Collection getEntitiesByClass(Class clazz) { - Collection list = new ArrayList(); - - world.getEntities().a().forEach((entity) -> { - Entity bukkitEntity = ((net.minecraft.world.entity.Entity) entity).getBukkitEntity(); - - if (bukkitEntity == null) { - return; - } - - Class bukkitClass = bukkitEntity.getClass(); - - if (clazz.isAssignableFrom(bukkitClass) && bukkitEntity.isValid()) { - list.add((T) bukkitEntity); - } - }); - - return list; + public Iterable getNMSEntities() { + return getHandle().getEntities().a(); } @Override - public Collection getEntitiesByClasses(Class... classes) { - Collection list = new ArrayList(); - - world.getEntities().a().forEach((entity) -> { - Entity bukkitEntity = ((net.minecraft.world.entity.Entity) entity).getBukkitEntity(); - - if (bukkitEntity == null) { - return; - } - - Class bukkitClass = bukkitEntity.getClass(); - - for (Class clazz : classes) { - if (clazz.isAssignableFrom(bukkitClass)) { - if (bukkitEntity.isValid()) { - list.add(bukkitEntity); - } - break; - } - } - }); - - return list; + public void addEntityToWorld(net.minecraft.world.entity.Entity entity, SpawnReason reason) { + getHandle().addEntity(entity, reason); } @Override @@ -1410,16 +1111,6 @@ public class CraftWorld implements World { } } - @Override - public T spawn(Location location, Class clazz) throws IllegalArgumentException { - return spawn(location, clazz, null, SpawnReason.CUSTOM); - } - - @Override - public T spawn(Location location, Class clazz, Consumer function) throws IllegalArgumentException { - return spawn(location, clazz, function, SpawnReason.CUSTOM); - } - @Override public FallingBlock spawnFallingBlock(Location location, MaterialData data) throws IllegalArgumentException { Validate.notNull(data, "MaterialData cannot be null"); @@ -1451,401 +1142,6 @@ public class CraftWorld implements World { return (FallingBlock) entity.getBukkitEntity(); } - @SuppressWarnings("unchecked") - public net.minecraft.world.entity.Entity createEntity(Location location, Class clazz) throws IllegalArgumentException { - if (location == null || clazz == null) { - throw new IllegalArgumentException("Location or entity class cannot be null"); - } - - net.minecraft.world.entity.Entity entity = null; - - double x = location.getX(); - double y = location.getY(); - double z = location.getZ(); - float pitch = location.getPitch(); - float yaw = location.getYaw(); - - // order is important for some of these - if (Boat.class.isAssignableFrom(clazz)) { - entity = new EntityBoat(world, x, y, z); - entity.setPositionRotation(x, y, z, yaw, pitch); - } else if (FallingBlock.class.isAssignableFrom(clazz)) { - entity = new EntityFallingBlock(world, x, y, z, world.getType(new BlockPosition(x, y, z))); - } else if (Projectile.class.isAssignableFrom(clazz)) { - if (Snowball.class.isAssignableFrom(clazz)) { - entity = new EntitySnowball(world, x, y, z); - } else if (Egg.class.isAssignableFrom(clazz)) { - entity = new EntityEgg(world, x, y, z); - } else if (AbstractArrow.class.isAssignableFrom(clazz)) { - if (TippedArrow.class.isAssignableFrom(clazz)) { - entity = EntityTypes.ARROW.a(world); - ((EntityTippedArrow) entity).setType(CraftPotionUtil.fromBukkit(new PotionData(PotionType.WATER, false, false))); - } else if (SpectralArrow.class.isAssignableFrom(clazz)) { - entity = EntityTypes.SPECTRAL_ARROW.a(world); - } else if (Trident.class.isAssignableFrom(clazz)) { - entity = EntityTypes.TRIDENT.a(world); - } else { - entity = EntityTypes.ARROW.a(world); - } - entity.setPositionRotation(x, y, z, 0, 0); - } else if (ThrownExpBottle.class.isAssignableFrom(clazz)) { - entity = EntityTypes.EXPERIENCE_BOTTLE.a(world); - entity.setPositionRotation(x, y, z, 0, 0); - } else if (EnderPearl.class.isAssignableFrom(clazz)) { - entity = EntityTypes.ENDER_PEARL.a(world); - entity.setPositionRotation(x, y, z, 0, 0); - } else if (ThrownPotion.class.isAssignableFrom(clazz)) { - if (LingeringPotion.class.isAssignableFrom(clazz)) { - entity = new EntityPotion(world, x, y, z); - ((EntityPotion) entity).setItem(CraftItemStack.asNMSCopy(new ItemStack(org.bukkit.Material.LINGERING_POTION, 1))); - } else { - entity = new EntityPotion(world, x, y, z); - ((EntityPotion) entity).setItem(CraftItemStack.asNMSCopy(new ItemStack(org.bukkit.Material.SPLASH_POTION, 1))); - } - } else if (Fireball.class.isAssignableFrom(clazz)) { - if (SmallFireball.class.isAssignableFrom(clazz)) { - entity = EntityTypes.SMALL_FIREBALL.a(world); - } else if (WitherSkull.class.isAssignableFrom(clazz)) { - entity = EntityTypes.WITHER_SKULL.a(world); - } else if (DragonFireball.class.isAssignableFrom(clazz)) { - entity = EntityTypes.DRAGON_FIREBALL.a(world); - } else { - entity = EntityTypes.FIREBALL.a(world); - } - entity.setPositionRotation(x, y, z, yaw, pitch); - Vector direction = location.getDirection().multiply(10); - ((EntityFireball) entity).setDirection(direction.getX(), direction.getY(), direction.getZ()); - } else if (ShulkerBullet.class.isAssignableFrom(clazz)) { - entity = EntityTypes.SHULKER_BULLET.a(world); - entity.setPositionRotation(x, y, z, yaw, pitch); - } else if (LlamaSpit.class.isAssignableFrom(clazz)) { - entity = EntityTypes.LLAMA_SPIT.a(world); - entity.setPositionRotation(x, y, z, yaw, pitch); - } else if (Firework.class.isAssignableFrom(clazz)) { - entity = new EntityFireworks(world, x, y, z, net.minecraft.world.item.ItemStack.EMPTY); - } - } else if (Minecart.class.isAssignableFrom(clazz)) { - if (PoweredMinecart.class.isAssignableFrom(clazz)) { - entity = new EntityMinecartFurnace(world, x, y, z); - } else if (StorageMinecart.class.isAssignableFrom(clazz)) { - entity = new EntityMinecartChest(world, x, y, z); - } else if (ExplosiveMinecart.class.isAssignableFrom(clazz)) { - entity = new EntityMinecartTNT(world, x, y, z); - } else if (HopperMinecart.class.isAssignableFrom(clazz)) { - entity = new EntityMinecartHopper(world, x, y, z); - } else if (SpawnerMinecart.class.isAssignableFrom(clazz)) { - entity = new EntityMinecartMobSpawner(world, x, y, z); - } else if (CommandMinecart.class.isAssignableFrom(clazz)) { - entity = new EntityMinecartCommandBlock(world, x, y, z); - } else { // Default to rideable minecart for pre-rideable compatibility - entity = new EntityMinecartRideable(world, x, y, z); - } - } else if (EnderSignal.class.isAssignableFrom(clazz)) { - entity = new EntityEnderSignal(world, x, y, z); - } else if (EnderCrystal.class.isAssignableFrom(clazz)) { - entity = EntityTypes.END_CRYSTAL.a(world); - entity.setPositionRotation(x, y, z, 0, 0); - } else if (LivingEntity.class.isAssignableFrom(clazz)) { - if (Chicken.class.isAssignableFrom(clazz)) { - entity = EntityTypes.CHICKEN.a(world); - } else if (Cow.class.isAssignableFrom(clazz)) { - if (MushroomCow.class.isAssignableFrom(clazz)) { - entity = EntityTypes.MOOSHROOM.a(world); - } else { - entity = EntityTypes.COW.a(world); - } - } else if (Golem.class.isAssignableFrom(clazz)) { - if (Snowman.class.isAssignableFrom(clazz)) { - entity = EntityTypes.SNOW_GOLEM.a(world); - } else if (IronGolem.class.isAssignableFrom(clazz)) { - entity = EntityTypes.IRON_GOLEM.a(world); - } else if (Shulker.class.isAssignableFrom(clazz)) { - entity = EntityTypes.SHULKER.a(world); - } - } else if (Creeper.class.isAssignableFrom(clazz)) { - entity = EntityTypes.CREEPER.a(world); - } else if (Ghast.class.isAssignableFrom(clazz)) { - entity = EntityTypes.GHAST.a(world); - } else if (Pig.class.isAssignableFrom(clazz)) { - entity = EntityTypes.PIG.a(world); - } else if (Player.class.isAssignableFrom(clazz)) { - // need a net server handler for this one - } else if (Sheep.class.isAssignableFrom(clazz)) { - entity = EntityTypes.SHEEP.a(world); - } else if (AbstractHorse.class.isAssignableFrom(clazz)) { - if (ChestedHorse.class.isAssignableFrom(clazz)) { - if (Donkey.class.isAssignableFrom(clazz)) { - entity = EntityTypes.DONKEY.a(world); - } else if (Mule.class.isAssignableFrom(clazz)) { - entity = EntityTypes.MULE.a(world); - } else if (Llama.class.isAssignableFrom(clazz)) { - if (TraderLlama.class.isAssignableFrom(clazz)) { - entity = EntityTypes.TRADER_LLAMA.a(world); - } else { - entity = EntityTypes.LLAMA.a(world); - } - } - } else if (SkeletonHorse.class.isAssignableFrom(clazz)) { - entity = EntityTypes.SKELETON_HORSE.a(world); - } else if (ZombieHorse.class.isAssignableFrom(clazz)) { - entity = EntityTypes.ZOMBIE_HORSE.a(world); - } else { - entity = EntityTypes.HORSE.a(world); - } - } else if (AbstractSkeleton.class.isAssignableFrom(clazz)) { - if (Stray.class.isAssignableFrom(clazz)) { - entity = EntityTypes.STRAY.a(world); - } else if (WitherSkeleton.class.isAssignableFrom(clazz)) { - entity = EntityTypes.WITHER_SKELETON.a(world); - } else if (Skeleton.class.isAssignableFrom(clazz)) { - entity = EntityTypes.SKELETON.a(world); - } - } else if (Slime.class.isAssignableFrom(clazz)) { - if (MagmaCube.class.isAssignableFrom(clazz)) { - entity = EntityTypes.MAGMA_CUBE.a(world); - } else { - entity = EntityTypes.SLIME.a(world); - } - } else if (Spider.class.isAssignableFrom(clazz)) { - if (CaveSpider.class.isAssignableFrom(clazz)) { - entity = EntityTypes.CAVE_SPIDER.a(world); - } else { - entity = EntityTypes.SPIDER.a(world); - } - } else if (Squid.class.isAssignableFrom(clazz)) { - if (GlowSquid.class.isAssignableFrom(clazz)) { - entity = EntityTypes.GLOW_SQUID.a(world); - } else { - entity = EntityTypes.SQUID.a(world); - } - } else if (Tameable.class.isAssignableFrom(clazz)) { - if (Wolf.class.isAssignableFrom(clazz)) { - entity = EntityTypes.WOLF.a(world); - } else if (Parrot.class.isAssignableFrom(clazz)) { - entity = EntityTypes.PARROT.a(world); - } else if (Cat.class.isAssignableFrom(clazz)) { - entity = EntityTypes.CAT.a(world); - } - } else if (PigZombie.class.isAssignableFrom(clazz)) { - entity = EntityTypes.ZOMBIFIED_PIGLIN.a(world); - } else if (Zombie.class.isAssignableFrom(clazz)) { - if (Husk.class.isAssignableFrom(clazz)) { - entity = EntityTypes.HUSK.a(world); - } else if (ZombieVillager.class.isAssignableFrom(clazz)) { - entity = EntityTypes.ZOMBIE_VILLAGER.a(world); - } else if (Drowned.class.isAssignableFrom(clazz)) { - entity = EntityTypes.DROWNED.a(world); - } else { - entity = new EntityZombie(world); - } - } else if (Giant.class.isAssignableFrom(clazz)) { - entity = EntityTypes.GIANT.a(world); - } else if (Silverfish.class.isAssignableFrom(clazz)) { - entity = EntityTypes.SILVERFISH.a(world); - } else if (Enderman.class.isAssignableFrom(clazz)) { - entity = EntityTypes.ENDERMAN.a(world); - } else if (Blaze.class.isAssignableFrom(clazz)) { - entity = EntityTypes.BLAZE.a(world); - } else if (AbstractVillager.class.isAssignableFrom(clazz)) { - if (Villager.class.isAssignableFrom(clazz)) { - entity = EntityTypes.VILLAGER.a(world); - } else if (WanderingTrader.class.isAssignableFrom(clazz)) { - entity = EntityTypes.WANDERING_TRADER.a(world); - } - } else if (Witch.class.isAssignableFrom(clazz)) { - entity = EntityTypes.WITCH.a(world); - } else if (Wither.class.isAssignableFrom(clazz)) { - entity = EntityTypes.WITHER.a(world); - } else if (ComplexLivingEntity.class.isAssignableFrom(clazz)) { - if (EnderDragon.class.isAssignableFrom(clazz)) { - entity = EntityTypes.ENDER_DRAGON.a(world); - } - } else if (Ambient.class.isAssignableFrom(clazz)) { - if (Bat.class.isAssignableFrom(clazz)) { - entity = EntityTypes.BAT.a(world); - } - } else if (Rabbit.class.isAssignableFrom(clazz)) { - entity = EntityTypes.RABBIT.a(world); - } else if (Endermite.class.isAssignableFrom(clazz)) { - entity = EntityTypes.ENDERMITE.a(world); - } else if (Guardian.class.isAssignableFrom(clazz)) { - if (ElderGuardian.class.isAssignableFrom(clazz)) { - entity = EntityTypes.ELDER_GUARDIAN.a(world); - } else { - entity = EntityTypes.GUARDIAN.a(world); - } - } else if (ArmorStand.class.isAssignableFrom(clazz)) { - entity = new EntityArmorStand(world, x, y, z); - } else if (PolarBear.class.isAssignableFrom(clazz)) { - entity = EntityTypes.POLAR_BEAR.a(world); - } else if (Vex.class.isAssignableFrom(clazz)) { - entity = EntityTypes.VEX.a(world); - } else if (Illager.class.isAssignableFrom(clazz)) { - if (Spellcaster.class.isAssignableFrom(clazz)) { - if (Evoker.class.isAssignableFrom(clazz)) { - entity = EntityTypes.EVOKER.a(world); - } else if (Illusioner.class.isAssignableFrom(clazz)) { - entity = EntityTypes.ILLUSIONER.a(world); - } - } else if (Vindicator.class.isAssignableFrom(clazz)) { - entity = EntityTypes.VINDICATOR.a(world); - } else if (Pillager.class.isAssignableFrom(clazz)) { - entity = EntityTypes.PILLAGER.a(world); - } - } else if (Turtle.class.isAssignableFrom(clazz)) { - entity = EntityTypes.TURTLE.a(world); - } else if (Phantom.class.isAssignableFrom(clazz)) { - entity = EntityTypes.PHANTOM.a(world); - } else if (Fish.class.isAssignableFrom(clazz)) { - if (Cod.class.isAssignableFrom(clazz)) { - entity = EntityTypes.COD.a(world); - } else if (PufferFish.class.isAssignableFrom(clazz)) { - entity = EntityTypes.PUFFERFISH.a(world); - } else if (Salmon.class.isAssignableFrom(clazz)) { - entity = EntityTypes.SALMON.a(world); - } else if (TropicalFish.class.isAssignableFrom(clazz)) { - entity = EntityTypes.TROPICAL_FISH.a(world); - } - } else if (Dolphin.class.isAssignableFrom(clazz)) { - entity = EntityTypes.DOLPHIN.a(world); - } else if (Ocelot.class.isAssignableFrom(clazz)) { - entity = EntityTypes.OCELOT.a(world); - } else if (Ravager.class.isAssignableFrom(clazz)) { - entity = EntityTypes.RAVAGER.a(world); - } else if (Panda.class.isAssignableFrom(clazz)) { - entity = EntityTypes.PANDA.a(world); - } else if (Fox.class.isAssignableFrom(clazz)) { - entity = EntityTypes.FOX.a(world); - } else if (Bee.class.isAssignableFrom(clazz)) { - entity = EntityTypes.BEE.a(world); - } else if (Hoglin.class.isAssignableFrom(clazz)) { - entity = EntityTypes.HOGLIN.a(world); - } else if (Piglin.class.isAssignableFrom(clazz)) { - entity = EntityTypes.PIGLIN.a(world); - } else if (PiglinBrute.class.isAssignableFrom(clazz)) { - entity = EntityTypes.PIGLIN_BRUTE.a(world); - } else if (Strider.class.isAssignableFrom(clazz)) { - entity = EntityTypes.STRIDER.a(world); - } else if (Zoglin.class.isAssignableFrom(clazz)) { - entity = EntityTypes.ZOGLIN.a(world); - } else if (Axolotl.class.isAssignableFrom(clazz)) { - entity = EntityTypes.AXOLOTL.a(world); - } else if (Goat.class.isAssignableFrom(clazz)) { - entity = EntityTypes.GOAT.a(world); - } - - if (entity != null) { - entity.setLocation(x, y, z, yaw, pitch); - entity.setHeadRotation(yaw); // SPIGOT-3587 - } - } else if (Hanging.class.isAssignableFrom(clazz)) { - BlockFace face = BlockFace.SELF; - - int width = 16; // 1 full block, also painting smallest size. - int height = 16; // 1 full block, also painting smallest size. - - if (ItemFrame.class.isAssignableFrom(clazz)) { - width = 12; - height = 12; - } else if (LeashHitch.class.isAssignableFrom(clazz)) { - width = 9; - height = 9; - } - - BlockFace[] faces = new BlockFace[]{BlockFace.EAST, BlockFace.NORTH, BlockFace.WEST, BlockFace.SOUTH, BlockFace.UP, BlockFace.DOWN}; - final BlockPosition pos = new BlockPosition(x, y, z); - for (BlockFace dir : faces) { - IBlockData nmsBlock = world.getType(pos.shift(CraftBlock.blockFaceToNotch(dir))); - if (nmsBlock.getMaterial().isBuildable() || BlockDiodeAbstract.isDiode(nmsBlock)) { - boolean taken = false; - AxisAlignedBB bb = (ItemFrame.class.isAssignableFrom(clazz)) - ? EntityItemFrame.calculateBoundingBox(null, pos, CraftBlock.blockFaceToNotch(dir).opposite(), width, height) - : EntityHanging.calculateBoundingBox(null, pos, CraftBlock.blockFaceToNotch(dir).opposite(), width, height); - List list = (List) world.getEntities(null, bb); - for (Iterator it = list.iterator(); !taken && it.hasNext();) { - net.minecraft.world.entity.Entity e = it.next(); - if (e instanceof EntityHanging) { - taken = true; // Hanging entities do not like hanging entities which intersect them. - } - } - - if (!taken) { - face = dir; - break; - } - } - } - - if (LeashHitch.class.isAssignableFrom(clazz)) { - entity = new EntityLeash(world, new BlockPosition(x, y, z)); - } else { - // No valid face found - Preconditions.checkArgument(face != BlockFace.SELF, "Cannot spawn hanging entity for %s at %s (no free face)", clazz.getName(), location); - - EnumDirection dir = CraftBlock.blockFaceToNotch(face).opposite(); - if (Painting.class.isAssignableFrom(clazz)) { - entity = new EntityPainting(world, new BlockPosition(x, y, z), dir); - } else if (ItemFrame.class.isAssignableFrom(clazz)) { - if (GlowItemFrame.class.isAssignableFrom(clazz)) { - entity = new net.minecraft.world.entity.decoration.GlowItemFrame(world, new BlockPosition(x, y, z), dir); - } else { - entity = new EntityItemFrame(world, new BlockPosition(x, y, z), dir); - } - } - } - - if (entity != null && !((EntityHanging) entity).survives()) { - throw new IllegalArgumentException("Cannot spawn hanging entity for " + clazz.getName() + " at " + location); - } - } else if (TNTPrimed.class.isAssignableFrom(clazz)) { - entity = new EntityTNTPrimed(world, x, y, z, null); - } else if (ExperienceOrb.class.isAssignableFrom(clazz)) { - entity = new EntityExperienceOrb(world, x, y, z, 0); - } else if (LightningStrike.class.isAssignableFrom(clazz)) { - entity = EntityTypes.LIGHTNING_BOLT.a(world); - } else if (AreaEffectCloud.class.isAssignableFrom(clazz)) { - entity = new EntityAreaEffectCloud(world, x, y, z); - } else if (EvokerFangs.class.isAssignableFrom(clazz)) { - entity = new EntityEvokerFangs(world, x, y, z, (float) Math.toRadians(yaw), 0, null); - } else if (Marker.class.isAssignableFrom(clazz)) { - entity = EntityTypes.MARKER.a(world); - entity.setPosition(x, y, z); - } - - if (entity != null) { - return entity; - } - - throw new IllegalArgumentException("Cannot spawn an entity for " + clazz.getName()); - } - - @SuppressWarnings("unchecked") - public T addEntity(net.minecraft.world.entity.Entity entity, SpawnReason reason) throws IllegalArgumentException { - return addEntity(entity, reason, null); - } - - @SuppressWarnings("unchecked") - public T addEntity(net.minecraft.world.entity.Entity entity, SpawnReason reason, Consumer function) throws IllegalArgumentException { - Preconditions.checkArgument(entity != null, "Cannot spawn null entity"); - - if (entity instanceof EntityInsentient) { - ((EntityInsentient) entity).prepare(getHandle(), getHandle().getDamageScaler(entity.getChunkCoordinates()), EnumMobSpawn.COMMAND, (GroupDataEntity) null, null); - } - - if (function != null) { - function.accept((T) entity.getBukkitEntity()); - } - - world.addEntity(entity, reason); - return (T) entity.getBukkitEntity(); - } - - public T spawn(Location location, Class clazz, Consumer function, SpawnReason reason) throws IllegalArgumentException { - net.minecraft.world.entity.Entity entity = createEntity(location, clazz); - - return addEntity(entity, reason, function); - } - @Override public ChunkSnapshot getEmptyChunkSnapshot(int x, int z, boolean includeBiome, boolean includeBiomeTempRain) { return CraftChunk.getEmptyChunkSnapshot(x, z, this, includeBiome, includeBiomeTempRain); diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBarrel.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBarrel.java index 54ba3aca2..300923cd0 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBarrel.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBarrel.java @@ -43,7 +43,9 @@ public class CraftBarrel extends CraftLootable implements Barr if (!open) { getTileEntity().setOpenFlag(blockData, true); - getTileEntity().playOpenSound(blockData, SoundEffects.BARREL_OPEN); + if (getWorldHandle() instanceof net.minecraft.world.level.World) { + getTileEntity().playOpenSound(blockData, SoundEffects.BARREL_OPEN); + } } } getTileEntity().openersCounter.opened = true; @@ -55,7 +57,9 @@ public class CraftBarrel extends CraftLootable implements Barr if (getTileEntity().openersCounter.opened) { IBlockData blockData = getTileEntity().getBlock(); getTileEntity().setOpenFlag(blockData, false); - getTileEntity().playOpenSound(blockData, SoundEffects.BARREL_CLOSE); + if (getWorldHandle() instanceof net.minecraft.world.level.World) { + getTileEntity().playOpenSound(blockData, SoundEffects.BARREL_CLOSE); + } } getTileEntity().openersCounter.opened = false; } diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBeacon.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBeacon.java index 5dc372e7b..0e74937a6 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBeacon.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBeacon.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.block; +import com.google.common.base.Preconditions; import java.util.ArrayList; import java.util.Collection; import net.minecraft.world.ChestLock; @@ -27,6 +28,8 @@ public class CraftBeacon extends CraftBlockEntityState impleme @Override public Collection getEntitiesInRange() { + Preconditions.checkState(getWorldHandle() instanceof net.minecraft.world.level.World, "Can't get entities during world generation"); + TileEntity tileEntity = this.getTileEntityFromWorld(); if (tileEntity instanceof TileEntityBeacon) { TileEntityBeacon beacon = (TileEntityBeacon) tileEntity; diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java index ed334ebad..3474ca6c1 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java @@ -65,6 +65,8 @@ public class CraftBeehive extends CraftBlockEntityState imple @Override public List releaseEntities() { + Preconditions.checkState(getWorldHandle() instanceof net.minecraft.world.level.World, "Can't release entities during world generation"); + List bees = new ArrayList<>(); if (isPlaced()) { diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java index 66f7471fb..bb31c71ac 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java @@ -81,6 +81,10 @@ public class CraftBlock implements Block { return position; } + public GeneratorAccess getHandle() { + return world; + } + @Override public World getWorld() { return world.getMinecraftWorld().getWorld(); @@ -198,7 +202,7 @@ public class CraftBlock implements Block { return world.setTypeAndData(position, blockData, 3); } else { boolean success = world.setTypeAndData(position, blockData, 2 | 16 | 1024); // NOTIFY | NO_OBSERVER | NO_PLACE (custom) - if (success) { + if (success && world instanceof net.minecraft.world.level.World) { world.getMinecraftWorld().notify( position, old, diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java index 3cbcc7211..43d8c9787 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java @@ -22,8 +22,7 @@ public class CraftBlockEntityState extends CraftBlockState this.tileEntityClass = tileEntityClass; // get tile entity from block: - CraftWorld world = (CraftWorld) this.getWorld(); - this.tileEntity = tileEntityClass.cast(world.getHandle().getTileEntity(this.getPosition())); + this.tileEntity = tileEntityClass.cast(getWorldHandle().getTileEntity(this.getPosition())); Preconditions.checkState(this.tileEntity != null, "Tile is null, asynchronous access? %s", block); // copy tile entity data: @@ -74,7 +73,7 @@ public class CraftBlockEntityState extends CraftBlockState protected TileEntity getTileEntityFromWorld() { requirePlaced(); - return ((CraftWorld) this.getWorld()).getHandle().getTileEntity(this.getPosition()); + return getWorldHandle().getTileEntity(this.getPosition()); } // gets the NBT data of the TileEntity represented by this block state diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java index d62b23a6f..153591593 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java @@ -1,6 +1,7 @@ package org.bukkit.craftbukkit.block; import com.google.common.base.Preconditions; +import java.lang.ref.WeakReference; import java.util.List; import net.minecraft.core.BlockPosition; import net.minecraft.world.level.GeneratorAccess; @@ -25,12 +26,20 @@ public class CraftBlockState implements BlockState { private final BlockPosition position; protected IBlockData data; protected int flag; + private WeakReference weakWorld; public CraftBlockState(final Block block) { this.world = (CraftWorld) block.getWorld(); this.position = ((CraftBlock) block).getPosition(); this.data = ((CraftBlock) block).getNMS(); this.flag = 3; + + GeneratorAccess generatorAccess = ((CraftBlock) block).getHandle(); + if (generatorAccess instanceof net.minecraft.world.level.World) { + this.weakWorld = null; + } else { + this.weakWorld = new WeakReference<>(generatorAccess); + } } public CraftBlockState(final Block block, int flag) { @@ -42,6 +51,7 @@ public class CraftBlockState implements BlockState { world = null; data = CraftMagicNumbers.getBlock(material).getBlockData(); position = BlockPosition.ZERO; + this.weakWorld = null; } public static CraftBlockState getBlockState(GeneratorAccess world, net.minecraft.core.BlockPosition pos) { @@ -52,6 +62,20 @@ public class CraftBlockState implements BlockState { return new CraftBlockState(CraftBlock.at(world, pos), flag); } + public GeneratorAccess getWorldHandle() { + if (weakWorld == null) { + return world.getHandle(); + } + + GeneratorAccess access = weakWorld.get(); + if (access == null) { + weakWorld = null; + return world.getHandle(); + } + + return access; + } + @Override public World getWorld() { requirePlaced(); @@ -154,7 +178,7 @@ public class CraftBlockState implements BlockState { @Override public CraftBlock getBlock() { requirePlaced(); - return CraftBlock.at(world.getHandle(), position); + return CraftBlock.at(getWorldHandle(), position); } @Override @@ -172,6 +196,7 @@ public class CraftBlockState implements BlockState { if (!isPlaced()) { return true; } + GeneratorAccess access = getWorldHandle(); CraftBlock block = getBlock(); if (block.getType() != getType()) { @@ -182,12 +207,14 @@ public class CraftBlockState implements BlockState { IBlockData newBlock = this.data; block.setTypeAndData(newBlock, applyPhysics); - world.getHandle().notify( - position, - block.getNMS(), - newBlock, - 3 - ); + if (access instanceof net.minecraft.world.level.World) { + world.getHandle().notify( + position, + block.getNMS(), + newBlock, + 3 + ); + } // Update levers etc if (false && applyPhysics && getData() instanceof Attachable) { // Call does not map to new API diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java b/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java index 81ecc0912..63b6589f9 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.block; +import com.google.common.base.Preconditions; import net.minecraft.sounds.SoundEffects; import net.minecraft.world.ITileInventory; import net.minecraft.world.level.block.BlockChest; @@ -40,6 +41,8 @@ public class CraftChest extends CraftLootable implements Chest @Override public Inventory getInventory() { + Preconditions.checkState(getWorldHandle() instanceof net.minecraft.world.level.World, "Can't get inventory during world generation, use getBlockInventory() instead"); + CraftInventory inventory = (CraftInventory) this.getBlockInventory(); if (!isPlaced()) { return inventory; @@ -60,7 +63,7 @@ public class CraftChest extends CraftLootable implements Chest @Override public void open() { requirePlaced(); - if (!getTileEntity().openersCounter.opened) { + if (!getTileEntity().openersCounter.opened && getWorldHandle() instanceof net.minecraft.world.level.World) { IBlockData block = getTileEntity().getBlock(); getTileEntity().getWorld().playBlockAction(getPosition(), block.getBlock(), 1, getTileEntity().openersCounter.getOpenerCount() + 1); TileEntityChest.playOpenSound(getTileEntity().getWorld(), getPosition(), block, SoundEffects.CHEST_OPEN); @@ -71,7 +74,7 @@ public class CraftChest extends CraftLootable implements Chest @Override public void close() { requirePlaced(); - if (getTileEntity().openersCounter.opened) { + if (getTileEntity().openersCounter.opened && getWorldHandle() instanceof net.minecraft.world.level.World) { IBlockData block = getTileEntity().getBlock(); getTileEntity().getWorld().playBlockAction(getPosition(), block.getBlock(), 1, 0); TileEntityChest.playOpenSound(getTileEntity().getWorld(), getPosition(), block, SoundEffects.CHEST_CLOSE); diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftDispenser.java b/src/main/java/org/bukkit/craftbukkit/block/CraftDispenser.java index 2176969fd..afb619fc7 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftDispenser.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftDispenser.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.block; +import com.google.common.base.Preconditions; import net.minecraft.world.level.block.BlockDispenser; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.TileEntityDispenser; @@ -49,6 +50,8 @@ public class CraftDispenser extends CraftLootable implement @Override public boolean dispense() { + Preconditions.checkState(getWorldHandle() instanceof net.minecraft.world.level.World, "Can't dispense during world generation"); + Block block = getBlock(); if (block.getType() == Material.DISPENSER) { diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftDropper.java b/src/main/java/org/bukkit/craftbukkit/block/CraftDropper.java index 561e2d01d..9f0598036 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftDropper.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftDropper.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.block; +import com.google.common.base.Preconditions; import net.minecraft.world.level.block.BlockDropper; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.TileEntityDropper; @@ -36,6 +37,8 @@ public class CraftDropper extends CraftLootable implements Dr @Override public void drop() { + Preconditions.checkState(getWorldHandle() instanceof net.minecraft.world.level.World, "Can't drop during world generation"); + Block block = getBlock(); if (block.getType() == Material.DROPPER) { diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftJukebox.java b/src/main/java/org/bukkit/craftbukkit/block/CraftJukebox.java index 8ea318a86..c3b8c7950 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftJukebox.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftJukebox.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.block; +import com.google.common.base.Preconditions; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.BlockJukeBox; import net.minecraft.world.level.block.Blocks; @@ -31,9 +32,9 @@ public class CraftJukebox extends CraftBlockEntityState imple CraftWorld world = (CraftWorld) this.getWorld(); Material record = this.getPlaying(); if (record == Material.AIR) { - world.getHandle().setTypeAndData(this.getPosition(), Blocks.JUKEBOX.getBlockData().set(BlockJukeBox.HAS_RECORD, false), 3); + getWorldHandle().setTypeAndData(this.getPosition(), Blocks.JUKEBOX.getBlockData().set(BlockJukeBox.HAS_RECORD, false), 3); } else { - world.getHandle().setTypeAndData(this.getPosition(), Blocks.JUKEBOX.getBlockData().set(BlockJukeBox.HAS_RECORD, true), 3); + getWorldHandle().setTypeAndData(this.getPosition(), Blocks.JUKEBOX.getBlockData().set(BlockJukeBox.HAS_RECORD, true), 3); } world.playEffect(this.getLocation(), Effect.RECORD_PLAY, record); } @@ -84,6 +85,8 @@ public class CraftJukebox extends CraftBlockEntityState imple @Override public boolean eject() { + Preconditions.checkState(getWorldHandle() instanceof net.minecraft.world.level.World, "Can't eject during world generation"); + requirePlaced(); TileEntity tileEntity = this.getTileEntityFromWorld(); if (!(tileEntity instanceof TileEntityJukeBox)) return false; diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftLectern.java b/src/main/java/org/bukkit/craftbukkit/block/CraftLectern.java index a80e13e6a..5286c4ca2 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftLectern.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftLectern.java @@ -46,7 +46,7 @@ public class CraftLectern extends CraftBlockEntityState imple public boolean update(boolean force, boolean applyPhysics) { boolean result = super.update(force, applyPhysics); - if (result && this.isPlaced() && this.getType() == Material.LECTERN) { + if (result && this.isPlaced() && this.getType() == Material.LECTERN && getWorldHandle() instanceof net.minecraft.world.level.World) { BlockLectern.a(this.world.getHandle(), this.getPosition(), this.getHandle()); } diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftShulkerBox.java b/src/main/java/org/bukkit/craftbukkit/block/CraftShulkerBox.java index 86fd466f7..ea226d6e3 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftShulkerBox.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftShulkerBox.java @@ -48,7 +48,7 @@ public class CraftShulkerBox extends CraftLootable impleme @Override public void open() { requirePlaced(); - if (!getTileEntity().opened) { + if (!getTileEntity().opened && getWorldHandle() instanceof net.minecraft.world.level.World) { World world = getTileEntity().getWorld(); world.playBlockAction(getPosition(), getTileEntity().getBlock().getBlock(), 1, 1); world.playSound(null, getPosition(), SoundEffects.SHULKER_BOX_OPEN, SoundCategory.BLOCKS, 0.5F, world.random.nextFloat() * 0.1F + 0.9F); @@ -59,7 +59,7 @@ public class CraftShulkerBox extends CraftLootable impleme @Override public void close() { requirePlaced(); - if (getTileEntity().opened) { + if (getTileEntity().opened && getWorldHandle() instanceof net.minecraft.world.level.World) { World world = getTileEntity().getWorld(); world.playBlockAction(getPosition(), getTileEntity().getBlock().getBlock(), 1, 0); world.playSound(null, getPosition(), SoundEffects.SHULKER_BOX_OPEN, SoundCategory.BLOCKS, 0.5F, world.random.nextFloat() * 0.1F + 0.9F); diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftStructureBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftStructureBlock.java index 02482904d..858876bfa 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftStructureBlock.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftStructureBlock.java @@ -180,9 +180,19 @@ public class CraftStructureBlock extends CraftBlockEntityState= 0, "ticks < 0"); diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java index 6fc198d70..04fea7c0b 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -506,6 +506,8 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { // Let the server handle cross world teleports if (!location.getWorld().equals(getWorld())) { + // Prevent teleportation to an other world during world generation + Preconditions.checkState(!entity.generation, "Cannot teleport entity to an other world during world generation"); entity.teleportTo(((CraftWorld) location.getWorld()).getHandle(), new BlockPosition(location.getX(), location.getY(), location.getZ())); return true; } @@ -530,6 +532,8 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { @Override public List getNearbyEntities(double x, double y, double z) { + Preconditions.checkState(!entity.generation, "Cannot get nearby entities during world generation"); + List notchEntityList = entity.level.getEntities(entity, entity.getBoundingBox().grow(x, y, z), Predicates.alwaysTrue()); List bukkitEntityList = new java.util.ArrayList(notchEntityList.size()); @@ -730,6 +734,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { @Override public void playEffect(EntityEffect type) { Preconditions.checkArgument(type != null, "type"); + Preconditions.checkState(!entity.generation, "Cannot play effect during world generation"); if (type.getApplicable().isInstance(this)) { this.getHandle().level.broadcastEntityEffect(getHandle(), type.getData()); diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHanging.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHanging.java index b597f268f..a98f98171 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftHanging.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHanging.java @@ -42,7 +42,7 @@ public class CraftHanging extends CraftEntity implements Hanging { getHandle().setDirection(EnumDirection.EAST); break; } - if (!force && !hanging.survives()) { + if (!force && !getHandle().generation && !hanging.survives()) { // Revert since it doesn't fit hanging.setDirection(dir); return false; diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java index ec0fa8482..9d4789ac0 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java @@ -27,7 +27,7 @@ public class CraftItemFrame extends CraftHanging implements ItemFrame { EnumDirection newDir = CraftBlock.blockFaceToNotch(face); getHandle().setDirection(newDir); - if (!force && !hanging.survives()) { + if (!force && !getHandle().generation && !hanging.survives()) { hanging.setDirection(oldDir); return false; } @@ -47,7 +47,9 @@ public class CraftItemFrame extends CraftHanging implements ItemFrame { } // update redstone - getHandle().getWorld().updateAdjacentComparators(getHandle().pos, Blocks.AIR); + if (!getHandle().generation) { + getHandle().getWorld().updateAdjacentComparators(getHandle().pos, Blocks.AIR); + } } @Override @@ -57,7 +59,8 @@ public class CraftItemFrame extends CraftHanging implements ItemFrame { @Override public void setItem(org.bukkit.inventory.ItemStack item, boolean playSound) { - getHandle().setItem(CraftItemStack.asNMSCopy(item), true, playSound); + // only updated redstone and play sound when it is not in generation + getHandle().setItem(CraftItemStack.asNMSCopy(item), !getHandle().generation, !getHandle().generation && playSound); } @Override diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java index 987173e86..f5683567f 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java @@ -114,6 +114,12 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { throw new IllegalArgumentException("Health must be between 0 and " + getMaxHealth() + "(" + health + ")"); } + // during world generation, we don't want to run logic for dropping items and xp + if (getHandle().generation && health == 0) { + getHandle().die(); + return; + } + getHandle().setHealth((float) health); if (health == 0) { @@ -165,6 +171,8 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { } private List getLineOfSight(Set transparent, int maxDistance, int maxLength) { + Preconditions.checkState(!getHandle().generation, "Cannot get line of sight during world generation"); + if (transparent == null) { transparent = Sets.newHashSet(Material.AIR, Material.CAVE_AIR, Material.VOID_AIR); } @@ -221,6 +229,8 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { @Override public RayTraceResult rayTraceBlocks(double maxDistance, FluidCollisionMode fluidCollisionMode) { + Preconditions.checkState(!getHandle().generation, "Cannot ray tray blocks during world generation"); + Location eyeLocation = this.getEyeLocation(); Vector direction = eyeLocation.getDirection(); return this.getWorld().rayTraceBlocks(eyeLocation, direction, maxDistance, fluidCollisionMode, false); @@ -274,6 +284,8 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { @Override public void damage(double amount, org.bukkit.entity.Entity source) { + Preconditions.checkState(!getHandle().generation, "Cannot damage entity during world generation"); + DamageSource reason = DamageSource.GENERIC; if (source instanceof HumanEntity) { @@ -394,6 +406,8 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { @Override @SuppressWarnings("unchecked") public T launchProjectile(Class projectile, Vector velocity) { + Preconditions.checkState(!getHandle().generation, "Cannot launch projectile during world generation"); + net.minecraft.world.level.World world = ((CraftWorld) getWorld()).getHandle(); net.minecraft.world.entity.Entity launch = null; @@ -486,6 +500,8 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { @Override public boolean hasLineOfSight(Entity other) { + Preconditions.checkState(!getHandle().generation, "Cannot check line of sight during world generation"); + return getHandle().hasLineOfSight(((CraftEntity) other).getHandle()); } @@ -559,7 +575,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { @Override public boolean setLeashHolder(Entity holder) { - if ((getHandle() instanceof EntityWither) || !(getHandle() instanceof EntityInsentient)) { + if (getHandle().generation || (getHandle() instanceof EntityWither) || !(getHandle() instanceof EntityInsentient)) { return false; } @@ -608,6 +624,8 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { @Override public boolean isClimbing() { + Preconditions.checkState(!getHandle().generation, "Cannot check if climbing during world generation"); + return getHandle().isClimbing(); } @@ -631,17 +649,22 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { @Override public void attack(Entity target) { Preconditions.checkArgument(target != null, "target == null"); + Preconditions.checkState(!getHandle().generation, "Cannot attack during world generation"); getHandle().attackEntity(((CraftEntity) target).getHandle()); } @Override public void swingMainHand() { + Preconditions.checkState(!getHandle().generation, "Cannot swing hand during world generation"); + getHandle().swingHand(EnumHand.MAIN_HAND, true); } @Override public void swingOffHand() { + Preconditions.checkState(!getHandle().generation, "Cannot swing hand during world generation"); + getHandle().swingHand(EnumHand.OFF_HAND, true); } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java index a3c80bc71..f7ebca6ae 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.entity; +import com.google.common.base.Preconditions; import net.minecraft.world.entity.EntityInsentient; import org.bukkit.Bukkit; import org.bukkit.NamespacedKey; @@ -16,6 +17,8 @@ public abstract class CraftMob extends CraftLivingEntity implements Mob { @Override public void setTarget(LivingEntity target) { + Preconditions.checkState(!getHandle().generation, "Cannot set target during world generation"); + EntityInsentient entity = getHandle(); if (target == null) { entity.setGoalTarget(null, null, false); diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPainting.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPainting.java index 4ef1b6173..ee489171a 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPainting.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPainting.java @@ -32,7 +32,7 @@ public class CraftPainting extends CraftHanging implements Painting { Paintings oldArt = painting.motive; painting.motive = CraftArt.BukkitToNotch(art); painting.setDirection(painting.getDirection()); - if (!force && !painting.survives()) { + if (!force && !getHandle().generation && !painting.survives()) { // Revert painting since it doesn't fit painting.motive = oldArt; painting.setDirection(painting.getDirection()); diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java index 41ca56f62..1efa3b4bc 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java @@ -1,5 +1,6 @@ package org.bukkit.craftbukkit.entity; +import com.google.common.base.Preconditions; import net.minecraft.world.entity.projectile.EntityShulkerBullet; import org.bukkit.craftbukkit.CraftServer; import org.bukkit.entity.Entity; @@ -35,6 +36,8 @@ public class CraftShulkerBullet extends AbstractProjectile implements ShulkerBul @Override public void setTarget(org.bukkit.entity.Entity target) { + Preconditions.checkState(!getHandle().generation, "Cannot set target during world generation"); + getHandle().setTarget(target == null ? null : ((CraftEntity) target).getHandle()); } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java index b987ba3df..aaf225e26 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java @@ -89,6 +89,7 @@ public class CraftVillager extends CraftAbstractVillager implements Villager { Preconditions.checkArgument(location != null, "Location cannot be null"); Preconditions.checkArgument(location.getWorld() != null, "Location needs to be in a world"); Preconditions.checkArgument(location.getWorld().equals(getWorld()), "Cannot sleep across worlds"); + Preconditions.checkState(!getHandle().generation, "Cannot sleep during world generation"); BlockPosition position = new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ()); IBlockData iblockdata = getHandle().level.getType(position); @@ -103,6 +104,7 @@ public class CraftVillager extends CraftAbstractVillager implements Villager { @Override public void wakeup() { Preconditions.checkState(isSleeping(), "Cannot wakeup if not sleeping"); + Preconditions.checkState(!getHandle().generation, "Cannot wakeup during world generation"); getHandle().entityWakeup(); } diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java index d36a1e25f..aea17496a 100644 --- a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java +++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java @@ -1,14 +1,20 @@ package org.bukkit.craftbukkit.generator; -import java.util.HashSet; -import java.util.Set; +import java.lang.ref.WeakReference; import net.minecraft.core.BlockPosition; +import net.minecraft.core.IRegistry; +import net.minecraft.world.level.biome.BiomeBase; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.ITileEntity; +import net.minecraft.world.level.block.entity.TileEntity; import net.minecraft.world.level.block.state.IBlockData; -import net.minecraft.world.level.chunk.ChunkSection; +import net.minecraft.world.level.chunk.BiomeStorage; +import net.minecraft.world.level.chunk.IChunkAccess; import org.bukkit.Material; import org.bukkit.World; +import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.block.CraftBlock; import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.craftbukkit.util.CraftMagicNumbers; import org.bukkit.generator.ChunkGenerator; @@ -18,20 +24,37 @@ import org.bukkit.material.MaterialData; * Data to be used for the block types and data in a newly generated chunk. */ public final class CraftChunkData implements ChunkGenerator.ChunkData { - private final int minHeight; private final int maxHeight; - private final ChunkSection[] sections; - private Set tiles; - private final Set lights = new HashSet<>(); + private final int minHeight; + private final WeakReference weakChunk; - public CraftChunkData(World world) { - this(world.getMinHeight(), world.getMaxHeight()); + public CraftChunkData(World world, IChunkAccess chunkAccess) { + this(world.getMaxHeight(), world.getMinHeight(), chunkAccess); } - /* pp for tests */ CraftChunkData(int minHeight, int maxHeight) { - this.minHeight = minHeight; + CraftChunkData(int maxHeight, int minHeight, IChunkAccess chunkAccess) { this.maxHeight = maxHeight; - sections = new ChunkSection[(((maxHeight - 1) >> 4) + 1) - (minHeight >> 4)]; + this.minHeight = minHeight; + this.weakChunk = new WeakReference<>(chunkAccess); + } + + public IChunkAccess getHandle() { + IChunkAccess access = weakChunk.get(); + + if (access == null) { + throw new IllegalStateException("IChunkAccess no longer present, are you using it in a different tick?"); + } + + return access; + } + + public void breakLink() { + weakChunk.clear(); + } + + @Override + public int getMaxHeight() { + return maxHeight; } @Override @@ -40,8 +63,9 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData { } @Override - public int getMaxHeight() { - return maxHeight; + public Biome getBiome(int x, int y, int z) { + BiomeStorage biomeStorage = getHandle().getBiomeIndex(); + return CraftBlock.biomeBaseToBiome((IRegistry) biomeStorage.biomeRegistry, biomeStorage.getBiome(x >> 2, y >> 2, z >> 2)); } @Override @@ -116,11 +140,9 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData { return; } for (int y = yMin; y < yMax; y++) { - ChunkSection section = getChunkSection(y, true); - int offsetBase = y & 0xf; for (int x = xMin; x < xMax; x++) { for (int z = zMin; z < zMax; z++) { - section.setType(x, offsetBase, z, type); + setBlock(x, y, z, type); } } } @@ -130,12 +152,9 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData { if (x != (x & 0xf) || y < minHeight || y >= maxHeight || z != (z & 0xf)) { return Blocks.AIR.getBlockData(); } - ChunkSection section = getChunkSection(y, false); - if (section == null) { - return Blocks.AIR.getBlockData(); - } else { - return section.getType(x, y & 0xf, z); - } + + IChunkAccess access = getHandle(); + return access.getType(new BlockPosition(access.getPos().d() + x, y, access.getPos().e() + z)); } @Override @@ -147,43 +166,22 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData { if (x != (x & 0xf) || y < minHeight || y >= maxHeight || z != (z & 0xf)) { return; } - ChunkSection section = getChunkSection(y, true); - section.setType(x, y & 0xf, z, type); - // SPIGOT-1753: Capture light blocks, for light updates - if (type.f() > 0) { // PAIL rename getLightEmission - lights.add(new BlockPosition(x, y, z)); - } else { - lights.remove(new BlockPosition(x, y, z)); - } + IChunkAccess access = getHandle(); + BlockPosition blockPosition = new BlockPosition(access.getPos().d() + x, y, access.getPos().e() + z); + IBlockData oldBlockData = access.setType(blockPosition, type, false); if (type.isTileEntity()) { - if (tiles == null) { - tiles = new HashSet<>(); + TileEntity tileEntity = ((ITileEntity) type.getBlock()).createTile(blockPosition, type); + + // createTile can return null, currently only the case with material MOVING_PISTON + if (tileEntity == null) { + access.removeTileEntity(blockPosition); + } else { + access.setTileEntity(tileEntity); } - - tiles.add(new BlockPosition(x, y, z)); + } else if (oldBlockData != null && oldBlockData.isTileEntity()) { + access.removeTileEntity(blockPosition); } } - - private ChunkSection getChunkSection(int y, boolean create) { - int offset = (y - minHeight) >> 4; - ChunkSection section = sections[offset]; - if (create && section == null) { - sections[offset] = section = new ChunkSection(offset + (minHeight >> 4)); - } - return section; - } - - ChunkSection[] getRawChunkData() { - return sections; - } - - Set getTiles() { - return tiles; - } - - Set getLights() { - return lights; - } } diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java new file mode 100644 index 000000000..20c433f71 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java @@ -0,0 +1,186 @@ +package org.bukkit.craftbukkit.generator; + +import com.google.common.base.Preconditions; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Random; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.server.level.RegionLimitedWorldAccess; +import net.minecraft.world.entity.EntityTypes; +import net.minecraft.world.level.GeneratorAccessSeed; +import net.minecraft.world.level.biome.BiomeBase; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.IChunkAccess; +import net.minecraft.world.level.chunk.ProtoChunk; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.TreeType; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.CraftRegionAccessor; +import org.bukkit.entity.Entity; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.generator.LimitedRegion; +import org.bukkit.util.BoundingBox; +import org.bukkit.util.Consumer; + +public class CraftLimitedRegion extends CraftRegionAccessor implements LimitedRegion { + + private final WeakReference weakAccess; + private final int centerChunkX; + private final int centerChunkZ; + // Buffer is one chunk (16 blocks), can be seen in ChunkStatus#q + // there the order is {..., FEATURES, LIQUID_CARVERS, STRUCTURE_STARTS, ...} + private final int buffer = 16; + private final BoundingBox region; + // Minecraft saves the entities as NBTTagCompound during chunk generation. This causes that + // changes made to the returned bukkit entity are not saved. To combat this we keep them and + // save them when the population is finished. + private final List entities = new ArrayList<>(); + + public CraftLimitedRegion(RegionLimitedWorldAccess access) { + this.weakAccess = new WeakReference<>(access); + centerChunkX = access.a().x; // PAIL rename getCenter + centerChunkZ = access.a().z; // PAIL rename getCenter + + // load entities which are already present + for (int x = -(buffer >> 4); x <= (buffer >> 4); x++) { + for (int z = -(buffer >> 4); z <= (buffer >> 4); z++) { + ProtoChunk chunk = (ProtoChunk) access.getChunkAt(centerChunkX + x, centerChunkZ + z); + for (NBTTagCompound compound : chunk.z()) { // PAIL rename getGenerationEntities + EntityTypes.a(compound, access.getMinecraftWorld(), (entity) -> { // PAIL rename fromNBTTag + entity.generation = true; + entities.add(entity); + return entity; + }); + } + } + } + + World world = access.getMinecraftWorld().getWorld(); + int xCenter = centerChunkX << 4; + int zCenter = centerChunkZ << 4; + int xMin = xCenter - getBuffer(); + int zMin = zCenter - getBuffer(); + int xMax = xCenter + getBuffer() + 16; + int zMax = zCenter + getBuffer() + 16; + + this.region = new BoundingBox(xMin, world.getMinHeight(), zMin, xMax, world.getMaxHeight(), zMax); + } + + public GeneratorAccessSeed getHandle() { + GeneratorAccessSeed handle = weakAccess.get(); + + if (handle == null) { + throw new IllegalStateException("GeneratorAccessSeed no longer present, are you using it in a different tick?"); + } + + return handle; + } + + public void saveEntities() { + GeneratorAccessSeed access = getHandle(); + for (int x = -(buffer >> 4); x <= (buffer >> 4); x++) { + for (int z = -(buffer >> 4); z <= (buffer >> 4); z++) { + ProtoChunk chunk = (ProtoChunk) access.getChunkAt(centerChunkX + x, centerChunkZ + z); + chunk.z().clear(); // PAIL rename getGenerationEntities + } + } + + for (net.minecraft.world.entity.Entity entity : entities) { + if (entity.isAlive()) { + // check if entity is still in region or if it got teleported outside it + Preconditions.checkState(isInRegion((int) entity.locX(), (int) entity.locY(), (int) entity.locZ()), "Entity %s is not in the region", entity); + access.addEntity(entity); + } + } + } + + public void breakLink() { + weakAccess.clear(); + } + + @Override + public int getBuffer() { + return buffer; + } + + @Override + public boolean isInRegion(Location location) { + return isInRegion(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + } + + @Override + public boolean isInRegion(int x, int y, int z) { + return region.contains(x, y, z); + } + + @Override + public Biome getBiome(int x, int y, int z) { + Preconditions.checkArgument(isInRegion(x, y, z), "Coordinates %s, %s, %s are not in the region", x, y, z); + return super.getBiome(x, y, z); + } + + @Override + public void setBiome(int x, int y, int z, BiomeBase biomeBase) { + Preconditions.checkArgument(isInRegion(x, y, z), "Coordinates %s, %s, %s are not in the region", x, y, z); + IChunkAccess chunk = getHandle().getChunkAt(x >> 4, z >> 4, ChunkStatus.EMPTY); + chunk.getBiomeIndex().setBiome(x >> 2, y >> 2, z >> 2, biomeBase); + } + + @Override + public BlockState getBlockState(int x, int y, int z) { + Preconditions.checkArgument(isInRegion(x, y, z), "Coordinates %s, %s, %s are not in the region", x, y, z); + return super.getBlockState(x, y, z); + } + + @Override + public BlockData getBlockData(int x, int y, int z) { + Preconditions.checkArgument(isInRegion(x, y, z), "Coordinates %s, %s, %s are not in the region", x, y, z); + return super.getBlockData(x, y, z); + } + + @Override + public Material getType(int x, int y, int z) { + Preconditions.checkArgument(isInRegion(x, y, z), "Coordinates %s, %s, %s are not in the region", x, y, z); + return super.getType(x, y, z); + } + + @Override + public void setBlockData(int x, int y, int z, BlockData blockData) { + Preconditions.checkArgument(isInRegion(x, y, z), "Coordinates %s, %s, %s are not in the region", x, y, z); + super.setBlockData(x, y, z, blockData); + } + + @Override + public boolean generateTree(Location location, Random random, TreeType treeType) { + Preconditions.checkArgument(isInRegion(location), "Coordinates %s, %s, %s are not in the region", location.getBlockX(), location.getBlockY(), location.getBlockZ()); + return super.generateTree(location, random, treeType); + } + + @Override + public boolean generateTree(Location location, Random random, TreeType treeType, Consumer consumer) { + Preconditions.checkArgument(isInRegion(location), "Coordinates %s, %s, %s are not in the region", location.getBlockX(), location.getBlockY(), location.getBlockZ()); + return super.generateTree(location, random, treeType, consumer); + } + + @Override + public Collection getNMSEntities() { + return new ArrayList<>(entities); + } + + @Override + public T spawn(Location location, Class clazz, Consumer function, CreatureSpawnEvent.SpawnReason reason) throws IllegalArgumentException { + Preconditions.checkArgument(isInRegion(location), "Coordinates %s, %s, %s are not in the region", location.getBlockX(), location.getBlockY(), location.getBlockZ()); + return super.spawn(location, clazz, function, reason); + } + + @Override + public void addEntityToWorld(net.minecraft.world.entity.Entity entity, CreatureSpawnEvent.SpawnReason reason) { + entities.add(entity); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java new file mode 100644 index 000000000..4c0c5e4d1 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java @@ -0,0 +1,68 @@ +package org.bukkit.craftbukkit.generator; + +import java.util.UUID; +import net.minecraft.world.level.dimension.DimensionManager; +import net.minecraft.world.level.storage.Convertable; +import net.minecraft.world.level.storage.IWorldDataServer; +import net.minecraft.world.level.storage.WorldDataServer; +import org.bukkit.World; +import org.bukkit.craftbukkit.util.WorldUUID; +import org.bukkit.generator.WorldInfo; + +public class CraftWorldInfo implements WorldInfo { + + private final String name; + private final UUID uuid; + private final World.Environment environment; + private final long seed; + private final int minHeight; + private final int maxHeight; + + public CraftWorldInfo(IWorldDataServer worldDataServer, Convertable.ConversionSession session, World.Environment environment, DimensionManager dimensionManager) { + this.name = worldDataServer.getName(); + this.uuid = WorldUUID.getUUID(session.levelPath.toFile()); + this.environment = environment; + this.seed = ((WorldDataServer) worldDataServer).getGeneratorSettings().getSeed(); + this.minHeight = dimensionManager.getMinY(); + this.maxHeight = dimensionManager.getMinY() + dimensionManager.getHeight(); + } + + public CraftWorldInfo(String name, UUID uuid, World.Environment environment, long seed, int minHeight, int maxHeight) { + this.name = name; + this.uuid = uuid; + this.environment = environment; + this.seed = seed; + this.minHeight = minHeight; + this.maxHeight = maxHeight; + } + + @Override + public String getName() { + return name; + } + + @Override + public UUID getUID() { + return uuid; + } + + @Override + public World.Environment getEnvironment() { + return environment; + } + + @Override + public long getSeed() { + return seed; + } + + @Override + public int getMinHeight() { + return minHeight; + } + + @Override + public int getMaxHeight() { + return maxHeight; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java index 6dacc0038..d823f08ce 100644 --- a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java +++ b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java @@ -7,15 +7,11 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import net.minecraft.core.BlockPosition; import net.minecraft.core.IRegistry; -import net.minecraft.core.IRegistryCustom; import net.minecraft.server.level.RegionLimitedWorldAccess; import net.minecraft.server.level.WorldServer; import net.minecraft.util.random.WeightedRandomList; import net.minecraft.world.entity.EnumCreatureType; import net.minecraft.world.level.BlockColumn; -import net.minecraft.world.level.GeneratorAccess; -import net.minecraft.world.level.GeneratorAccessSeed; -import net.minecraft.world.level.IBlockAccess; import net.minecraft.world.level.LevelHeightAccessor; import net.minecraft.world.level.StructureManager; import net.minecraft.world.level.biome.BiomeBase; @@ -30,9 +26,10 @@ import net.minecraft.world.level.chunk.ChunkSection; import net.minecraft.world.level.chunk.IChunkAccess; import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.levelgen.HeightMap; +import net.minecraft.world.level.levelgen.SeededRandom; import net.minecraft.world.level.levelgen.WorldGenStage; -import net.minecraft.world.level.levelgen.structure.templatesystem.DefinedStructureManager; import org.bukkit.block.Biome; +import org.bukkit.craftbukkit.CraftHeightMap; import org.bukkit.craftbukkit.block.CraftBlock; import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator.BiomeGrid; @@ -44,7 +41,10 @@ public class CustomChunkGenerator extends InternalChunkGenerator { private final ChunkGenerator generator; private final WorldServer world; private final Random random = new Random(); + private boolean newApi; + private boolean implementBaseHeight = true; + @Deprecated private class CustomBiomeGrid implements BiomeGrid { private final BiomeStorage biome; // SPIGOT-5529: stored in 4x4 grid @@ -90,21 +90,11 @@ public class CustomChunkGenerator extends InternalChunkGenerator { return new CustomChunkGenerator(this.world, delegate.withSeed(i), this.generator); } - @Override - public void createBiomes(IRegistry iregistry, IChunkAccess ichunkaccess) { - // Don't allow the server to override any custom biomes that have been set - } - @Override public WorldChunkManager getWorldChunkManager() { return delegate.getWorldChunkManager(); } - @Override - public void storeStructures(GeneratorAccessSeed generatoraccessseed, StructureManager structuremanager, IChunkAccess ichunkaccess) { - delegate.storeStructures(generatoraccessseed, structuremanager, ichunkaccess); - } - @Override public int getSeaLevel() { return delegate.getSeaLevel(); @@ -112,25 +102,56 @@ public class CustomChunkGenerator extends InternalChunkGenerator { @Override public void buildBase(RegionLimitedWorldAccess regionlimitedworldaccess, IChunkAccess ichunkaccess) { - // Call the bukkit ChunkGenerator before structure generation so correct biome information is available. + if (generator.shouldGenerateSurface()) { + delegate.buildSurface(regionlimitedworldaccess, ichunkaccess); + } + + CraftChunkData chunkData = new CraftChunkData(this.world.getWorld(), ichunkaccess); + SeededRandom random = new SeededRandom(); int x = ichunkaccess.getPos().x; int z = ichunkaccess.getPos().z; - random.setSeed((long) x * 341873128712L + (long) z * 132897987541L); + random.a(x, z); // PAIL rename surfaceSeeded + generator.generateSurface(this.world.getWorld(), random, x, z, chunkData); + + if (generator.shouldGenerateBedrock()) { + random = new SeededRandom(); + random.a(x, z); // PAIL rename surfaceSeeded + delegate.buildBedrock(ichunkaccess, random); + } + + random = new SeededRandom(); + random.a(x, z); // PAIL rename surfaceSeeded + generator.generateBedrock(this.world.getWorld(), random, x, z, chunkData); + chunkData.breakLink(); + + // return if new api is used + if (newApi) { + return; + } + + // old ChunkGenerator logic, for backwards compatibility + // Call the bukkit ChunkGenerator before structure generation so correct biome information is available. + this.random.setSeed((long) x * 341873128712L + (long) z * 132897987541L); // Get default biome data for chunk CustomBiomeGrid biomegrid = new CustomBiomeGrid(new BiomeStorage(world.t().d(IRegistry.BIOME_REGISTRY), regionlimitedworldaccess, ichunkaccess.getPos(), this.getWorldChunkManager())); ChunkData data; - if (generator.isParallelCapable()) { - data = generator.generateChunkData(this.world.getWorld(), random, x, z, biomegrid); - } else { - synchronized (this) { - data = generator.generateChunkData(this.world.getWorld(), random, x, z, biomegrid); + try { + if (generator.isParallelCapable()) { + data = generator.generateChunkData(this.world.getWorld(), this.random, x, z, biomegrid); + } else { + synchronized (this) { + data = generator.generateChunkData(this.world.getWorld(), this.random, x, z, biomegrid); + } } + } catch (UnsupportedOperationException exception) { + newApi = true; + return; } - Preconditions.checkArgument(data instanceof CraftChunkData, "Plugins must use createChunkData(World) rather than implementing ChunkData: %s", data); - CraftChunkData craftData = (CraftChunkData) data; + Preconditions.checkArgument(data instanceof OldCraftChunkData, "Plugins must use createChunkData(World) rather than implementing ChunkData: %s", data); + OldCraftChunkData craftData = (OldCraftChunkData) data; ChunkSection[] sections = craftData.getRawChunkData(); ChunkSection[] csect = ichunkaccess.getSections(); @@ -170,31 +191,61 @@ public class CustomChunkGenerator extends InternalChunkGenerator { } @Override - public void createStructures(IRegistryCustom iregistrycustom, StructureManager structuremanager, IChunkAccess ichunkaccess, DefinedStructureManager definedstructuremanager, long i) { - if (generator.shouldGenerateStructures()) { - // Still need a way of getting the biome of this chunk to pass to createStructures - // Using default biomes for now. - delegate.createStructures(iregistrycustom, structuremanager, ichunkaccess, definedstructuremanager, i); + public void doCarving(long seed, BiomeManager biomemanager, IChunkAccess ichunkaccess, WorldGenStage.Features worldgenstage_features) { + if (generator.shouldGenerateCaves()) { + super.doCarving(seed, biomemanager, ichunkaccess, worldgenstage_features); } - } + if (worldgenstage_features == WorldGenStage.Features.LIQUID) { // stage check ensures that the method is only called once + CraftChunkData chunkData = new CraftChunkData(this.world.getWorld(), ichunkaccess); + SeededRandom random = new SeededRandom(); + int x = ichunkaccess.getPos().x; + int z = ichunkaccess.getPos().z; + random.c(seed, 0, 0); // PAIL rename carvingSeeded - @Override - public void doCarving(long i, BiomeManager biomemanager, IChunkAccess ichunkaccess, WorldGenStage.Features worldgenstage_features) { - if (generator.shouldGenerateCaves()) { - delegate.doCarving(i, biomemanager, ichunkaccess, worldgenstage_features); + generator.generateCaves(this.world.getWorld(), random, x, z, chunkData); + chunkData.breakLink(); } } @Override public CompletableFuture buildNoise(Executor executor, StructureManager structuremanager, IChunkAccess ichunkaccess) { - // Disable vanilla generation - return CompletableFuture.completedFuture(ichunkaccess); + CompletableFuture future = null; + if (generator.shouldGenerateNoise()) { + future = delegate.buildNoise(executor, structuremanager, ichunkaccess); + } + + java.util.function.Function function = (ichunkaccess1) -> { + CraftChunkData chunkData = new CraftChunkData(this.world.getWorld(), ichunkaccess1); + SeededRandom random = new SeededRandom(); + int x = ichunkaccess1.getPos().x; + int z = ichunkaccess1.getPos().z; + random.setSeed((long) x * 341873128712L + (long) z * 132897987541L); + + generator.generateNoise(this.world.getWorld(), random, x, z, chunkData); + chunkData.breakLink(); + return ichunkaccess1; + }; + + return future == null ? CompletableFuture.supplyAsync(() -> function.apply(ichunkaccess), net.minecraft.SystemUtils.f()) : future.thenApply(function); } @Override public int getBaseHeight(int i, int j, HeightMap.Type heightmap_type, LevelHeightAccessor levelheightaccessor) { - return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor); + if (implementBaseHeight) { + try { + SeededRandom random = new SeededRandom(); + int xChunk = i >> 4; + int zChunk = j >> 4; + random.setSeed((long) xChunk * 341873128712L + (long) zChunk * 132897987541L); + + return generator.getBaseHeight(this.world.getWorld(), random, i, j, CraftHeightMap.fromNMS(heightmap_type)); + } catch (UnsupportedOperationException exception) { + implementBaseHeight = false; + } + } + + return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor); } @Override @@ -204,9 +255,7 @@ public class CustomChunkGenerator extends InternalChunkGenerator { @Override public void addDecorations(RegionLimitedWorldAccess regionlimitedworldaccess, StructureManager structuremanager) { - if (generator.shouldGenerateDecorations()) { - delegate.addDecorations(regionlimitedworldaccess, structuremanager); - } + super.addDecorations(regionlimitedworldaccess, structuremanager, generator.shouldGenerateDecorations()); } @Override diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CustomWorldChunkManager.java b/src/main/java/org/bukkit/craftbukkit/generator/CustomWorldChunkManager.java new file mode 100644 index 000000000..5dd6454dd --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/generator/CustomWorldChunkManager.java @@ -0,0 +1,58 @@ +package org.bukkit.craftbukkit.generator; + +import com.google.common.base.Preconditions; +import com.mojang.serialization.Codec; +import java.util.ArrayList; +import java.util.List; +import net.minecraft.core.IRegistry; +import net.minecraft.world.level.biome.BiomeBase; +import net.minecraft.world.level.biome.WorldChunkManager; +import org.bukkit.block.Biome; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.generator.BiomeProvider; +import org.bukkit.generator.WorldInfo; + +public class CustomWorldChunkManager extends WorldChunkManager { + + private final WorldInfo worldInfo; + private final BiomeProvider biomeProvider; + private final IRegistry registry; + + private static List biomeListToBiomeBaseList(List biomes, IRegistry registry) { + List biomeBases = new ArrayList<>(); + + for (Biome biome : biomes) { + Preconditions.checkArgument(biome != Biome.CUSTOM, "Cannot use the biome %s", biome); + biomeBases.add(CraftBlock.biomeToBiomeBase(registry, biome)); + } + + return biomeBases; + } + + public CustomWorldChunkManager(WorldInfo worldInfo, BiomeProvider biomeProvider, IRegistry registry) { + super(biomeListToBiomeBaseList(biomeProvider.getBiomes(worldInfo), registry)); + + this.worldInfo = worldInfo; + this.biomeProvider = biomeProvider; + this.registry = registry; + } + + @Override + protected Codec a() { + throw new UnsupportedOperationException("Cannot serialize CustomWorldChunkManager"); + } + + @Override + public WorldChunkManager a(long l) { + // TODO check method further + throw new UnsupportedOperationException("Cannot copy CustomWorldChunkManager"); + } + + @Override + public BiomeBase getBiome(int x, int y, int z) { + Biome biome = biomeProvider.getBiome(worldInfo, x << 2, y << 2, z << 2); + Preconditions.checkArgument(biome != Biome.CUSTOM, "Cannot set the biome to %s", biome); + + return CraftBlock.biomeToBiomeBase(registry, biome); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java new file mode 100644 index 000000000..2f67f59c8 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java @@ -0,0 +1,196 @@ +package org.bukkit.craftbukkit.generator; + +import java.util.HashSet; +import java.util.Set; +import net.minecraft.core.BlockPosition; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.IBlockData; +import net.minecraft.world.level.chunk.ChunkSection; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.material.MaterialData; + +/** + * Data to be used for the block types and data in a newly generated chunk. + */ +@Deprecated +public final class OldCraftChunkData implements ChunkGenerator.ChunkData { + private final int minHeight; + private final int maxHeight; + private final ChunkSection[] sections; + private Set tiles; + private final Set lights = new HashSet<>(); + + public OldCraftChunkData(World world) { + this(world.getMinHeight(), world.getMaxHeight()); + } + + /* pp for tests */ OldCraftChunkData(int minHeight, int maxHeight) { + this.minHeight = minHeight; + this.maxHeight = maxHeight; + sections = new ChunkSection[(((maxHeight - 1) >> 4) + 1) - (minHeight >> 4)]; + } + + @Override + public int getMinHeight() { + return minHeight; + } + + @Override + public int getMaxHeight() { + return maxHeight; + } + + @Override + public Biome getBiome(int x, int y, int z) { + throw new UnsupportedOperationException("Unsupported, in older chunk generator api"); + } + + @Override + public void setBlock(int x, int y, int z, Material material) { + setBlock(x, y, z, material.createBlockData()); + } + + @Override + public void setBlock(int x, int y, int z, MaterialData material) { + setBlock(x, y, z, CraftMagicNumbers.getBlock(material)); + } + + @Override + public void setBlock(int x, int y, int z, BlockData blockData) { + setBlock(x, y, z, ((CraftBlockData) blockData).getState()); + } + + @Override + public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, Material material) { + setRegion(xMin, yMin, zMin, xMax, yMax, zMax, material.createBlockData()); + } + + @Override + public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, MaterialData material) { + setRegion(xMin, yMin, zMin, xMax, yMax, zMax, CraftMagicNumbers.getBlock(material)); + } + + @Override + public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockData blockData) { + setRegion(xMin, yMin, zMin, xMax, yMax, zMax, ((CraftBlockData) blockData).getState()); + } + + @Override + public Material getType(int x, int y, int z) { + return CraftMagicNumbers.getMaterial(getTypeId(x, y, z).getBlock()); + } + + @Override + public MaterialData getTypeAndData(int x, int y, int z) { + return CraftMagicNumbers.getMaterial(getTypeId(x, y, z)); + } + + @Override + public BlockData getBlockData(int x, int y, int z) { + return CraftBlockData.fromData(getTypeId(x, y, z)); + } + + public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, IBlockData type) { + // Clamp to sane values. + if (xMin > 0xf || yMin >= maxHeight || zMin > 0xf) { + return; + } + if (xMin < 0) { + xMin = 0; + } + if (yMin < minHeight) { + yMin = minHeight; + } + if (zMin < 0) { + zMin = 0; + } + if (xMax > 0x10) { + xMax = 0x10; + } + if (yMax > maxHeight) { + yMax = maxHeight; + } + if (zMax > 0x10) { + zMax = 0x10; + } + if (xMin >= xMax || yMin >= yMax || zMin >= zMax) { + return; + } + for (int y = yMin; y < yMax; y++) { + ChunkSection section = getChunkSection(y, true); + int offsetBase = y & 0xf; + for (int x = xMin; x < xMax; x++) { + for (int z = zMin; z < zMax; z++) { + section.setType(x, offsetBase, z, type); + } + } + } + } + + public IBlockData getTypeId(int x, int y, int z) { + if (x != (x & 0xf) || y < minHeight || y >= maxHeight || z != (z & 0xf)) { + return Blocks.AIR.getBlockData(); + } + ChunkSection section = getChunkSection(y, false); + if (section == null) { + return Blocks.AIR.getBlockData(); + } else { + return section.getType(x, y & 0xf, z); + } + } + + @Override + public byte getData(int x, int y, int z) { + return CraftMagicNumbers.toLegacyData(getTypeId(x, y, z)); + } + + private void setBlock(int x, int y, int z, IBlockData type) { + if (x != (x & 0xf) || y < minHeight || y >= maxHeight || z != (z & 0xf)) { + return; + } + ChunkSection section = getChunkSection(y, true); + section.setType(x, y & 0xf, z, type); + + // SPIGOT-1753: Capture light blocks, for light updates + if (type.f() > 0) { // PAIL rename getLightEmission + lights.add(new BlockPosition(x, y, z)); + } else { + lights.remove(new BlockPosition(x, y, z)); + } + + if (type.isTileEntity()) { + if (tiles == null) { + tiles = new HashSet<>(); + } + + tiles.add(new BlockPosition(x, y, z)); + } + } + + private ChunkSection getChunkSection(int y, boolean create) { + int offset = (y - minHeight) >> 4; + ChunkSection section = sections[offset]; + if (create && section == null) { + sections[offset] = section = new ChunkSection(offset + (minHeight >> 4)); + } + return section; + } + + ChunkSection[] getRawChunkData() { + return sections; + } + + Set getTiles() { + return tiles; + } + + Set getLights() { + return lights; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java b/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java index 133d0f85e..a5eeccb64 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java +++ b/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java @@ -4,22 +4,25 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Set; +import java.util.function.Predicate; import net.minecraft.core.BlockPosition; -import net.minecraft.world.level.World; +import net.minecraft.world.level.GeneratorAccess; import net.minecraft.world.level.block.state.IBlockData; +import net.minecraft.world.level.dimension.DimensionManager; import net.minecraft.world.level.material.Fluid; import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.block.CraftBlock; import org.bukkit.craftbukkit.block.CraftBlockState; public class BlockStateListPopulator extends DummyGeneratorAccess { - private final World world; + private final GeneratorAccess world; private final LinkedHashMap list; - public BlockStateListPopulator(World world) { + public BlockStateListPopulator(GeneratorAccess world) { this(world, new LinkedHashMap<>()); } - public BlockStateListPopulator(World world, LinkedHashMap list) { + public BlockStateListPopulator(GeneratorAccess world, LinkedHashMap list) { this.world = world; this.list = list; } @@ -38,7 +41,8 @@ public class BlockStateListPopulator extends DummyGeneratorAccess { @Override public boolean setTypeAndData(BlockPosition position, IBlockData data, int flag) { - CraftBlockState state = CraftBlockState.getBlockState(world, position, flag); + CraftBlockState state = (CraftBlockState) CraftBlock.at(world, position).getState(); + state.setFlag(flag); state.setData(data); // remove first to keep insertion order list.remove(position); @@ -60,7 +64,28 @@ public class BlockStateListPopulator extends DummyGeneratorAccess { return new ArrayList<>(list.values()); } - public World getWorld() { + public GeneratorAccess getWorld() { return world; } + + // For tree generation + @Override + public int getMinBuildHeight() { + return getWorld().getMinBuildHeight(); + } + + @Override + public int getHeight() { + return getWorld().getHeight(); + } + + @Override + public boolean a(BlockPosition blockposition, Predicate predicate) { + return predicate.test(getType(blockposition)); + } + + @Override + public DimensionManager getDimensionManager() { + return world.getDimensionManager(); + } } diff --git a/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java b/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java index a3005a730..4da1c8486 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java +++ b/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java @@ -3,9 +3,11 @@ package org.bukkit.craftbukkit.util; import java.util.List; import java.util.Random; import java.util.function.Predicate; +import java.util.stream.Stream; import net.minecraft.core.BlockPosition; import net.minecraft.core.EnumDirection; import net.minecraft.core.IRegistryCustom; +import net.minecraft.core.SectionPosition; import net.minecraft.core.particles.ParticleParam; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.WorldServer; @@ -14,7 +16,7 @@ import net.minecraft.sounds.SoundEffect; import net.minecraft.world.DifficultyDamageScaler; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.EntityHuman; -import net.minecraft.world.level.GeneratorAccess; +import net.minecraft.world.level.GeneratorAccessSeed; import net.minecraft.world.level.TickList; import net.minecraft.world.level.TickListEmpty; import net.minecraft.world.level.biome.BiomeBase; @@ -31,6 +33,8 @@ import net.minecraft.world.level.dimension.DimensionManager; import net.minecraft.world.level.entity.EntityTypeTest; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.levelgen.HeightMap; +import net.minecraft.world.level.levelgen.feature.StructureGenerator; +import net.minecraft.world.level.levelgen.structure.StructureStart; import net.minecraft.world.level.lighting.LightEngine; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.FluidType; @@ -38,9 +42,9 @@ import net.minecraft.world.level.material.FluidTypes; import net.minecraft.world.level.storage.WorldData; import net.minecraft.world.phys.AxisAlignedBB; -public class DummyGeneratorAccess implements GeneratorAccess { +public class DummyGeneratorAccess implements GeneratorAccessSeed { - public static final GeneratorAccess INSTANCE = new DummyGeneratorAccess(); + public static final GeneratorAccessSeed INSTANCE = new DummyGeneratorAccess(); protected DummyGeneratorAccess() { } @@ -100,6 +104,11 @@ public class DummyGeneratorAccess implements GeneratorAccess { throw new UnsupportedOperationException("Not supported yet."); } + @Override + public WorldServer getLevel() { + throw new UnsupportedOperationException("Not supported yet."); + } + @Override public WorldServer getMinecraftWorld() { throw new UnsupportedOperationException("Not supported yet."); @@ -219,4 +228,14 @@ public class DummyGeneratorAccess implements GeneratorAccess { public boolean a(BlockPosition blockposition, boolean flag, Entity entity, int i) { return false; // SPIGOT-6515 } + + @Override + public long getSeed() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Stream> a(SectionPosition sectionPosition, StructureGenerator structureGenerator) { + throw new UnsupportedOperationException("Not supported yet."); + } } diff --git a/src/test/java/org/bukkit/craftbukkit/generator/ChunkDataTest.java b/src/test/java/org/bukkit/craftbukkit/generator/ChunkDataTest.java index 40c324612..b1e25c3ae 100644 --- a/src/test/java/org/bukkit/craftbukkit/generator/ChunkDataTest.java +++ b/src/test/java/org/bukkit/craftbukkit/generator/ChunkDataTest.java @@ -11,12 +11,12 @@ public class ChunkDataTest extends AbstractTestingBase { private static final BlockData RED_WOOL = Material.RED_WOOL.createBlockData(); private static final BlockData AIR = Material.AIR.createBlockData(); - private boolean testSetBlock(CraftChunkData data, int x, int y, int z, BlockData type, BlockData expected) { + private boolean testSetBlock(OldCraftChunkData data, int x, int y, int z, BlockData type, BlockData expected) { data.setBlock(x, y, z, type); return expected.equals(data.getBlockData(x, y, z)) && expected.getMaterial().equals(data.getType(x, y, z)); } - private void testSetRegion(CraftChunkData data, int minx, int miny, int minz, int maxx, int maxy, int maxz, BlockData type) { + private void testSetRegion(OldCraftChunkData data, int minx, int miny, int minz, int maxx, int maxy, int maxz, BlockData type) { data.setRegion(minx, miny, minz, maxx, maxy, maxz, type); for (int y = 0; y < data.getMaxHeight(); y++) { for (int z = 0; z < 16; z++) { @@ -34,21 +34,21 @@ public class ChunkDataTest extends AbstractTestingBase { @Test public void testMinHeight() { - CraftChunkData data = new CraftChunkData(-128, 128); + OldCraftChunkData data = new OldCraftChunkData(-128, 128); assertTrue("Could not set block below min height", testSetBlock(data, 0, -256, 0, RED_WOOL, AIR)); assertTrue("Could set block above min height", testSetBlock(data, 0, -64, 0, RED_WOOL, RED_WOOL)); } @Test public void testMaxHeight() { - CraftChunkData data = new CraftChunkData(0, 128); + OldCraftChunkData data = new OldCraftChunkData(0, 128); assertTrue("Could not set block above max height", testSetBlock(data, 0, 128, 0, RED_WOOL, AIR)); assertTrue("Could set block below max height", testSetBlock(data, 0, 127, 0, RED_WOOL, RED_WOOL)); } @Test public void testBoundsCheckingSingle() { - CraftChunkData data = new CraftChunkData(0, 256); + OldCraftChunkData data = new OldCraftChunkData(0, 256); assertTrue("Can set block inside chunk bounds", testSetBlock(data, 0, 0, 0, RED_WOOL, RED_WOOL)); assertTrue("Can set block inside chunk bounds", testSetBlock(data, 15, 255, 15, RED_WOOL, RED_WOOL)); assertTrue("Can no set block outside chunk bounds", testSetBlock(data, -1, 0, 0, RED_WOOL, AIR)); @@ -61,7 +61,7 @@ public class ChunkDataTest extends AbstractTestingBase { @Test public void testSetRegion() { - CraftChunkData data = new CraftChunkData(0, 256); + OldCraftChunkData data = new OldCraftChunkData(0, 256); testSetRegion(data, -100, 0, -100, 0, 256, 0, RED_WOOL); // exclusively outside testSetRegion(data, 16, 256, 16, 0, 0, 0, RED_WOOL); // minimum >= maximum testSetRegion(data, 0, 0, 0, 0, 0, 0, RED_WOOL); // minimum == maximum