I'm using spigot for this project, but, as I'm working only with nms, I think that I can ask this question. In the prevoius versions of minecraft we could create a new WorldChunkManager and override the default one of a world with the one just created by using reflection ("c" variable in the WorldProviderClass). As Mojang changes every time the code, in 1.13 the variable c (WorldChunkManager) is found in a class called ChunkGeneratorAbstract that extends ChunkGenerator. From what I know there's only a way to get there: cast ChunkGeneratorAbstract to ChunkGenerator, and this is what I did in my code:
public class Utils {
private IslandCraftPlugin plugin = JavaPlugin.getPlugin(IslandCraftPlugin.class);
public void register(World world, final BiomeGenerator biome) throws NoSuchFieldException, IllegalAccessException {
CraftWorld craftWorld = (CraftWorld) world;
ChunkGeneratorAbstract generator = (ChunkGeneratorAbstract) craftWorld.getHandle().worldProvider.getChunkGenerator();
Field field = getField(generator.getClass(), "c");
field.setAccessible(true);
field.set(generator, new CustomWorldChunkManager(biome));
}
private Field getField(Class<?> clazz, String fieldName) throws NoSuchFieldException {
try {
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
Class<?> superClass = clazz.getSuperclass();
if (superClass == null) {
throw e;
} else {
return getField(superClass, fieldName);
}
}
}
}
, but when I try :
craftWorld.getHandle().worldProvider.getChunkGenerator().getWorldChunkManager() instanceof CustomWorldChunkManager
I get a false but when I try to check if the field was correctly set I get true. What am I doing wrong?
PS:
If you need it, here's the ChunkGeneratorAbstract class:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package net.minecraft.server.v1_13_R1;
import com.google.common.collect.Maps;
import java.util.BitSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.function.Function;
import javax.annotation.Nullable;
import net.minecraft.server.v1_13_R1.BlockPosition.MutableBlockPosition;
import net.minecraft.server.v1_13_R1.HeightMap.Type;
import net.minecraft.server.v1_13_R1.WorldGenStage.Decoration;
import net.minecraft.server.v1_13_R1.WorldGenStage.Features;
import org.bukkit.craftbukkit.libs.it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import org.bukkit.craftbukkit.libs.it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
import org.bukkit.craftbukkit.libs.it.unimi.dsi.fastutil.longs.LongSet;
public abstract class ChunkGeneratorAbstract<C extends GeneratorSettings> implements ChunkGenerator<C> {
protected final GeneratorAccess a;
protected final long b;
protected final WorldChunkManager c;
protected final Map<StructureGenerator<? extends WorldGenFeatureConfiguration>, Long2ObjectMap<StructureStart>> d = Maps.newHashMap();
protected final Map<StructureGenerator<? extends WorldGenFeatureConfiguration>, Long2ObjectMap<LongSet>> e = Maps.newHashMap();
public ChunkGeneratorAbstract(GeneratorAccess generatoraccess, WorldChunkManager worldchunkmanager) {
this.a = generatoraccess;
this.b = generatoraccess.getSeed();
this.c = worldchunkmanager;
}
public void addFeatures(RegionLimitedWorldAccess regionlimitedworldaccess, Features worldgenstage_features) {
SeededRandom seededrandom = new SeededRandom(this.b);
int i = regionlimitedworldaccess.a();
int j = regionlimitedworldaccess.b();
BitSet bitset = regionlimitedworldaccess.c(i, j).a(worldgenstage_features);
for(int k = i - 8; k <= i + 8; ++k) {
for(int l = j - 8; l <= j + 8; ++l) {
List list = regionlimitedworldaccess.getChunkProvider().getChunkGenerator().getWorldChunkManager().getBiome(new BlockPosition(k * 16, 0, l * 16), (BiomeBase)null).a(worldgenstage_features);
ListIterator listiterator = list.listIterator();
while(listiterator.hasNext()) {
int i1 = listiterator.nextIndex();
WorldGenCarverWrapper worldgencarverwrapper = (WorldGenCarverWrapper)listiterator.next();
seededrandom.c(regionlimitedworldaccess.getMinecraftWorld().getSeed() + (long)i1, k, l);
if (worldgencarverwrapper.a(regionlimitedworldaccess, seededrandom, k, l, WorldGenFeatureConfiguration.e)) {
worldgencarverwrapper.a(regionlimitedworldaccess, seededrandom, k, l, i, j, bitset, WorldGenFeatureConfiguration.e);
}
}
}
}
}
@Nullable
public BlockPosition findNearestMapFeature(World world, String s, BlockPosition blockposition, int i) {
StructureGenerator structuregenerator = (StructureGenerator)WorldGenerator.aF.get(s.toLowerCase(Locale.ROOT));
return structuregenerator != null ? structuregenerator.getNearestGeneratedFeature(world, this, blockposition, i) : null;
}
protected void a(IChunkAccess ichunkaccess, Random random) {
MutableBlockPosition blockposition_mutableblockposition = new MutableBlockPosition();
int i = ichunkaccess.getPos().d();
int j = ichunkaccess.getPos().e();
Iterator iterator = BlockPosition.a(i, 0, j, i + 16, 0, j + 16).iterator();
while(iterator.hasNext()) {
BlockPosition blockposition = (BlockPosition)iterator.next();
for(int k = 4; k >= 0; --k) {
if (k <= random.nextInt(5)) {
ichunkaccess.a(blockposition_mutableblockposition.c(blockposition.getX(), k, blockposition.getZ()), Blocks.BEDROCK.getBlockData(), false);
}
}
}
}
public void addDecorations(RegionLimitedWorldAccess regionlimitedworldaccess) {
BlockFalling.instaFall = true;
int i = regionlimitedworldaccess.a();
int j = regionlimitedworldaccess.b();
int k = i * 16;
int l = j * 16;
BlockPosition blockposition = new BlockPosition(k, 0, l);
BiomeBase biomebase = regionlimitedworldaccess.c(i + 1, j + 1).getBiomeIndex()[0];
SeededRandom seededrandom = new SeededRandom();
long i1 = seededrandom.a(regionlimitedworldaccess.getSeed(), k, l);
Decoration[] aworldgenstage_decoration = Decoration.values();
int j1 = aworldgenstage_decoration.length;
for(int k1 = 0; k1 < j1; ++k1) {
Decoration worldgenstage_decoration = aworldgenstage_decoration[k1];
biomebase.a(worldgenstage_decoration, this, regionlimitedworldaccess, i1, seededrandom, blockposition);
}
BlockFalling.instaFall = false;
}
public void a(IChunkAccess ichunkaccess, BiomeBase[] abiomebase, SeededRandom seededrandom, int i) {
ChunkCoordIntPair chunkcoordintpair = ichunkaccess.getPos();
int j = chunkcoordintpair.d();
int k = chunkcoordintpair.e();
double[] adouble = this.a(chunkcoordintpair.x, chunkcoordintpair.z);
for(int l = 0; l < 16; ++l) {
for(int i1 = 0; i1 < 16; ++i1) {
int j1 = j + l;
int k1 = k + i1;
int l1 = ichunkaccess.a(Type.WORLD_SURFACE_WG, l, i1) + 1;
abiomebase[i1 * 16 + l].a(seededrandom, ichunkaccess, j1, k1, l1, adouble[i1 * 16 + l], this.getSettings().r(), this.getSettings().s(), i, this.a.getSeed());
}
}
}
public abstract C getSettings();
public abstract double[] a(int var1, int var2);
public boolean canSpawnStructure(BiomeBase biomebase, StructureGenerator<? extends WorldGenFeatureConfiguration> structuregenerator) {
return biomebase.a(structuregenerator);
}
@Nullable
public WorldGenFeatureConfiguration getFeatureConfiguration(BiomeBase biomebase, StructureGenerator<? extends WorldGenFeatureConfiguration> structuregenerator) {
return biomebase.b(structuregenerator);
}
public WorldChunkManager getWorldChunkManager() {
return this.c;
}
public long getSeed() {
return this.b;
}
public Long2ObjectMap<StructureStart> getStructureStartCache(StructureGenerator<? extends WorldGenFeatureConfiguration> structuregenerator) {
return (Long2ObjectMap)this.d.computeIfAbsent(structuregenerator, (s) -> {
return Long2ObjectMaps.synchronize(new ExpiringMap(8192, 10000));
});
}
public Long2ObjectMap<LongSet> getStructureCache(StructureGenerator<? extends WorldGenFeatureConfiguration> structuregenerator) {
return (Long2ObjectMap)this.e.computeIfAbsent(structuregenerator, (s) -> {
return Long2ObjectMaps.synchronize(new ExpiringMap(8192, 10000));
});
}
public int e() {
return 256;
}
public World getWorld() {
return this.a.getMinecraftWorld();
}
}
and here's the CHunkGenerator class:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package net.minecraft.server.v1_13_R1;
import java.util.List;
import javax.annotation.Nullable;
import net.minecraft.server.v1_13_R1.BiomeBase.BiomeMeta;
import net.minecraft.server.v1_13_R1.WorldGenStage.Features;
import org.bukkit.craftbukkit.libs.it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import org.bukkit.craftbukkit.libs.it.unimi.dsi.fastutil.longs.LongSet;
public interface ChunkGenerator<C extends GeneratorSettings> {
void createChunk(IChunkAccess var1);
void addFeatures(RegionLimitedWorldAccess var1, Features var2);
void addDecorations(RegionLimitedWorldAccess var1);
void addMobs(RegionLimitedWorldAccess var1);
List<BiomeMeta> getMobsFor(EnumCreatureType var1, BlockPosition var2);
@Nullable
BlockPosition findNearestMapFeature(World var1, String var2, BlockPosition var3, int var4);
C getSettings();
int a(World var1, boolean var2, boolean var3);
boolean canSpawnStructure(BiomeBase var1, StructureGenerator<? extends WorldGenFeatureConfiguration> var2);
@Nullable
WorldGenFeatureConfiguration getFeatureConfiguration(BiomeBase var1, StructureGenerator<? extends WorldGenFeatureConfiguration> var2);
Long2ObjectMap<StructureStart> getStructureStartCache(StructureGenerator<? extends WorldGenFeatureConfiguration> var1);
Long2ObjectMap<LongSet> getStructureCache(StructureGenerator<? extends WorldGenFeatureConfiguration> var1);
WorldChunkManager getWorldChunkManager();
long getSeed();
int getSpawnHeight();
int e();
World getWorld();
}