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