SPIGOT-5880, SPIGOT-5567: New ChunkGenerator API

## **Current API**
The current world generation API is very old and limited when you want to make more complex world generation. Resulting in some hard to fix bugs such as that you cannot modify blocks outside the chunk in the BlockPopulator (which should and was per the docs possible), or strange behavior such as SPIGOT-5880.

## **New API**
With the new API, the generation is more separate in multiple methods and is more in line with Vanilla chunk generation. The new API is designed to as future proof as possible. If for example a new generation step is added it can easily also be added as a step in API by simply creating the method for it. On the other side if a generation step gets removed, the method can easily be called after another, which is the case with surface and bedrock. The new API and changes are also fully backwards compatible with old chunk generators.

### **Changes in the new api**
**Extra generation steps:**
Noise, surface, bedrock and caves are added as steps. With those generation steps three extra methods for Vanilla generation are also added. Those new methods provide the ChunkData instead of returning one. The reason for this is, that the ChunkData is now backed by a ChunkAccess. With this, each step has the information of the step before and the Vanilla information (if chosen by setting a 'should' method to true). The old method is deprecated.

**New class BiomeProvider**
The BiomeProvider acts as Biome source and wrapper for the NMS class WorldChunkManager. With this the underlying Vanilla ChunkGeneration knows which Biome to use for the structure and decoration generation. (Fixes: SPIGOT-5880). Although the List of Biomes which is required in BiomeProvider, is currently not much in use in Vanilla, I decided to add it to future proof the API when it may be required in later versions of Minecraft.
The BiomeProvider is also separated from the ChunkGenerator for plugins which only want to change the biome map, such as single Biome worlds or if some biomes should be more present than others.

**Deprecated isParallelCapable**
Mojang has and is pushing to a more multi threaded chunk generation. This should also be the case for custom chunk generators. This is why the new API only supports multi threaded generation. This does not affect the old API, which is still checking this.

**Base height method added**
This method was added to also bring the Minecraft generator and Bukkit generator more in line. With this it is possible to return the max height of a location (before decorations). This is useful to let most structures know were to place them. This fixes SPIGOT-5567. (This fixes not all structures placement, desert pyramids for example are still way up at y-level 64, This however is more a vanilla bug and should be fixed at Mojangs end).

**WorldInfo Class**
The World object was swapped for a WorldInfo object. This is because many methods of the World object won't work during world generation and would mostly likely result in a deadlock. It contains any information a plugin should need to identify the world.

**BlockPopulator Changes**
Instead of directly manipulating a chunk, changes are now made to a new class LimitedRegion, this class provides methods to populated the chunk and its surrounding area. The wrapping is done so that the population can be moved into the place where Minecraft generates decorations. Where there is no chunk to access yet. By moving it into this place the generation is now async and the surrounding area of the chunk can also be used.

For common methods between the World and LimitedRegion a RegionAccessor was added.

By: DerFrZocker <derrieple@gmail.com>
This commit is contained in:
CraftBukkit/Spigot 2021-08-15 08:08:16 +10:00
parent fdefaeeccd
commit c2e4e91b1b
41 changed files with 2034 additions and 949 deletions

View file

@ -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<TickTask> 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 extends MinecraftServer> S a(Function<Thread, S> function) {
AtomicReference<S> 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<WorldDimension> registrymaterials = overworldSettings.d();
+ for (Entry<ResourceKey<WorldDimension>, 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<World> 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<Void> a(Collection<String> collection) {
CompletableFuture<Void> 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 @@
}

View file

@ -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<World> resourcekey, DimensionManager dimensionmanager, WorldLoadListener worldloadlistener, ChunkGenerator chunkgenerator, boolean flag, long i, List<MobSpawner> 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<World> resourcekey, DimensionManager dimensionmanager, WorldLoadListener worldloadlistener, ChunkGenerator chunkgenerator, boolean flag, long i, List<MobSpawner> 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());

View file

@ -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) {

View file

@ -66,9 +66,9 @@
+ return typeKey;
+ }
+
+ protected World(WorldDataMutable worlddatamutable, ResourceKey<World> resourcekey, final DimensionManager dimensionmanager, Supplier<GameProfilerFiller> supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.World.Environment env) {
+ protected World(WorldDataMutable worlddatamutable, ResourceKey<World> resourcekey, final DimensionManager dimensionmanager, Supplier<GameProfilerFiller> 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

View file

@ -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<ChunkCoordIntPair> 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<StructureFeature<?, ?>> supplier = (Supplier) iterator.next();
@ -18,7 +82,7 @@
}
}
@@ -364,9 +373,11 @@
@@ -364,9 +410,11 @@
}
static {

View file

@ -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<GeneratorSettingBase> settings;
+ public final Supplier<GeneratorSettingBase> 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) {

View file

@ -2,7 +2,7 @@ package org.bukkit.craftbukkit;
import org.bukkit.HeightMap;
final class CraftHeightMap {
public final class CraftHeightMap {
private CraftHeightMap() {
}

View file

@ -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<BlockState> 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<Entity> getEntities() {
List<Entity> list = new ArrayList<Entity>();
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<LivingEntity> getLivingEntities() {
List<LivingEntity> list = new ArrayList<LivingEntity>();
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 <T extends Entity> Collection<T> getEntitiesByClass(Class<T> clazz) {
Collection<T> list = new ArrayList<T>();
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<Entity> getEntitiesByClasses(Class<?>... classes) {
Collection<Entity> list = new ArrayList<Entity>();
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<net.minecraft.world.entity.Entity> getNMSEntities();
@Override
public <T extends Entity> T spawn(Location location, Class<T> clazz) throws IllegalArgumentException {
return spawn(location, clazz, null, CreatureSpawnEvent.SpawnReason.CUSTOM);
}
@Override
public <T extends Entity> T spawn(Location location, Class<T> clazz, Consumer<T> function) throws IllegalArgumentException {
return spawn(location, clazz, function, CreatureSpawnEvent.SpawnReason.CUSTOM);
}
public <T extends Entity> T spawn(Location location, Class<T> clazz, Consumer<T> function, CreatureSpawnEvent.SpawnReason reason) throws IllegalArgumentException {
net.minecraft.world.entity.Entity entity = createEntity(location, clazz);
return addEntity(entity, reason, function);
}
@SuppressWarnings("unchecked")
public <T extends Entity> T addEntity(net.minecraft.world.entity.Entity entity, CreatureSpawnEvent.SpawnReason reason) throws IllegalArgumentException {
return addEntity(entity, reason, null);
}
@SuppressWarnings("unchecked")
public <T extends Entity> T addEntity(net.minecraft.world.entity.Entity entity, CreatureSpawnEvent.SpawnReason reason, Consumer<T> 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<? extends Entity> 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<net.minecraft.world.entity.Entity> list = (List<net.minecraft.world.entity.Entity>) getHandle().getEntities(null, bb);
for (Iterator<net.minecraft.world.entity.Entity> 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());
}
}

View file

@ -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<WorldDimension> 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<net.minecraft.world.level.World> 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

View file

@ -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<BlockPopulator> populators = new ArrayList<BlockPopulator>();
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<BlockPopulator> 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<Entity> getEntities() {
List<Entity> list = new ArrayList<Entity>();
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<LivingEntity> getLivingEntities() {
List<LivingEntity> list = new ArrayList<LivingEntity>();
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 <T extends Entity> Collection<T> getEntitiesByClass(Class<T> clazz) {
Collection<T> list = new ArrayList<T>();
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<net.minecraft.world.entity.Entity> getNMSEntities() {
return getHandle().getEntities().a();
}
@Override
public Collection<Entity> getEntitiesByClasses(Class<?>... classes) {
Collection<Entity> list = new ArrayList<Entity>();
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 extends Entity> T spawn(Location location, Class<T> clazz) throws IllegalArgumentException {
return spawn(location, clazz, null, SpawnReason.CUSTOM);
}
@Override
public <T extends Entity> T spawn(Location location, Class<T> clazz, Consumer<T> 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<? extends Entity> 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<net.minecraft.world.entity.Entity> list = (List<net.minecraft.world.entity.Entity>) world.getEntities(null, bb);
for (Iterator<net.minecraft.world.entity.Entity> 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 extends Entity> T addEntity(net.minecraft.world.entity.Entity entity, SpawnReason reason) throws IllegalArgumentException {
return addEntity(entity, reason, null);
}
@SuppressWarnings("unchecked")
public <T extends Entity> T addEntity(net.minecraft.world.entity.Entity entity, SpawnReason reason, Consumer<T> 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 extends Entity> T spawn(Location location, Class<T> clazz, Consumer<T> 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);

View file

@ -43,7 +43,9 @@ public class CraftBarrel extends CraftLootable<TileEntityBarrel> 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<TileEntityBarrel> 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;
}

View file

@ -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<TileEntityBeacon> impleme
@Override
public Collection<LivingEntity> 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;

View file

@ -65,6 +65,8 @@ public class CraftBeehive extends CraftBlockEntityState<TileEntityBeehive> imple
@Override
public List<Bee> releaseEntities() {
Preconditions.checkState(getWorldHandle() instanceof net.minecraft.world.level.World, "Can't release entities during world generation");
List<Bee> bees = new ArrayList<>();
if (isPlaced()) {

View file

@ -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,

View file

@ -22,8 +22,7 @@ public class CraftBlockEntityState<T extends TileEntity> 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<T extends TileEntity> 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

View file

@ -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<GeneratorAccess> 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

View file

@ -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<TileEntityChest> 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<TileEntityChest> 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<TileEntityChest> 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);

View file

@ -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<TileEntityDispenser> 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) {

View file

@ -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<TileEntityDropper> 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) {

View file

@ -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<TileEntityJukeBox> 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<TileEntityJukeBox> 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;

View file

@ -46,7 +46,7 @@ public class CraftLectern extends CraftBlockEntityState<TileEntityLectern> 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());
}

View file

@ -48,7 +48,7 @@ public class CraftShulkerBox extends CraftLootable<TileEntityShulkerBox> 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<TileEntityShulkerBox> 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);

View file

@ -180,9 +180,19 @@ public class CraftStructureBlock extends CraftBlockEntityState<TileEntityStructu
@Override
protected void applyTo(TileEntityStructure tileEntity) {
super.applyTo(tileEntity);
net.minecraft.world.level.GeneratorAccess access = getWorldHandle();
// Ensure block type is correct
tileEntity.setUsageMode(tileEntity.getUsageMode());
if (access instanceof net.minecraft.world.level.World) {
tileEntity.setUsageMode(tileEntity.getUsageMode());
} else {
// Custom handle during world generation
// From TileEntityStructure#setUsageMode(BlockPropertyStructureMode)
net.minecraft.world.level.block.state.IBlockData data = access.getType(this.getPosition());
if (data.a(net.minecraft.world.level.block.Blocks.STRUCTURE_BLOCK)) {
access.setTypeAndData(this.getPosition(), data.set(net.minecraft.world.level.block.BlockStructure.MODE, tileEntity.getUsageMode()), 2);
}
}
}
private static boolean isBetween(int num, int min, int max) {

View file

@ -20,26 +20,20 @@ public class CraftCreeper extends CraftMonster implements Creeper {
@Override
public void setPowered(boolean powered) {
CraftServer server = this.server;
Creeper entity = (Creeper) this.getHandle().getBukkitEntity();
CreeperPowerEvent.PowerCause cause = powered ? CreeperPowerEvent.PowerCause.SET_ON : CreeperPowerEvent.PowerCause.SET_OFF;
if (powered) {
CreeperPowerEvent event = new CreeperPowerEvent(entity, CreeperPowerEvent.PowerCause.SET_ON);
server.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
getHandle().setPowered(true);
}
} else {
CreeperPowerEvent event = new CreeperPowerEvent(entity, CreeperPowerEvent.PowerCause.SET_OFF);
server.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
getHandle().setPowered(false);
}
// only call event when we are not in world generation
if (getHandle().generation || !callPowerEvent(cause)) {
getHandle().setPowered(powered);
}
}
private boolean callPowerEvent(CreeperPowerEvent.PowerCause cause) {
CreeperPowerEvent event = new CreeperPowerEvent((Creeper) getHandle().getBukkitEntity(), cause);
server.getPluginManager().callEvent(event);
return event.isCancelled();
}
@Override
public void setMaxFuseTicks(int ticks) {
Preconditions.checkArgument(ticks >= 0, "ticks < 0");

View file

@ -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<org.bukkit.entity.Entity> getNearbyEntities(double x, double y, double z) {
Preconditions.checkState(!entity.generation, "Cannot get nearby entities during world generation");
List<Entity> notchEntityList = entity.level.getEntities(entity, entity.getBoundingBox().grow(x, y, z), Predicates.alwaysTrue());
List<org.bukkit.entity.Entity> bukkitEntityList = new java.util.ArrayList<org.bukkit.entity.Entity>(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());

View file

@ -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;

View file

@ -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

View file

@ -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<Block> getLineOfSight(Set<Material> 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 extends Projectile> T launchProjectile(Class<? extends T> 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);
}

View file

@ -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);

View file

@ -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());

View file

@ -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());
}

View file

@ -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();
}

View file

@ -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<BlockPosition> tiles;
private final Set<BlockPosition> lights = new HashSet<>();
private final int minHeight;
private final WeakReference<IChunkAccess> 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<BiomeBase>) 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<BlockPosition> getTiles() {
return tiles;
}
Set<BlockPosition> getLights() {
return lights;
}
}

View file

@ -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<GeneratorAccessSeed> 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<net.minecraft.world.entity.Entity> 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<BlockState> 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<net.minecraft.world.entity.Entity> getNMSEntities() {
return new ArrayList<>(entities);
}
@Override
public <T extends Entity> T spawn(Location location, Class<T> clazz, Consumer<T> 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);
}
}

View file

@ -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;
}
}

View file

@ -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<BiomeBase> 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<IChunkAccess> buildNoise(Executor executor, StructureManager structuremanager, IChunkAccess ichunkaccess) {
// Disable vanilla generation
return CompletableFuture.completedFuture(ichunkaccess);
CompletableFuture<IChunkAccess> future = null;
if (generator.shouldGenerateNoise()) {
future = delegate.buildNoise(executor, structuremanager, ichunkaccess);
}
java.util.function.Function<IChunkAccess, IChunkAccess> 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

View file

@ -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<BiomeBase> registry;
private static List<BiomeBase> biomeListToBiomeBaseList(List<Biome> biomes, IRegistry<BiomeBase> registry) {
List<BiomeBase> 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<BiomeBase> registry) {
super(biomeListToBiomeBaseList(biomeProvider.getBiomes(worldInfo), registry));
this.worldInfo = worldInfo;
this.biomeProvider = biomeProvider;
this.registry = registry;
}
@Override
protected Codec<? extends WorldChunkManager> 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);
}
}

View file

@ -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<BlockPosition> tiles;
private final Set<BlockPosition> 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<BlockPosition> getTiles() {
return tiles;
}
Set<BlockPosition> getLights() {
return lights;
}
}

View file

@ -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<BlockPosition, CraftBlockState> list;
public BlockStateListPopulator(World world) {
public BlockStateListPopulator(GeneratorAccess world) {
this(world, new LinkedHashMap<>());
}
public BlockStateListPopulator(World world, LinkedHashMap<BlockPosition, CraftBlockState> list) {
public BlockStateListPopulator(GeneratorAccess world, LinkedHashMap<BlockPosition, CraftBlockState> 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<IBlockData> predicate) {
return predicate.test(getType(blockposition));
}
@Override
public DimensionManager getDimensionManager() {
return world.getDimensionManager();
}
}

View file

@ -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<? extends StructureStart<?>> a(SectionPosition sectionPosition, StructureGenerator<?> structureGenerator) {
throw new UnsupportedOperationException("Not supported yet.");
}
}

View file

@ -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