Refactored chunk and world generation handling

- common world generator class
- load default chunk date during generation
- prevent loading chunk with invalid data
- add a few logs to catch bad transitions
This commit is contained in:
LemADEC 2017-08-14 00:10:18 +02:00
parent 9bd098bd4f
commit aa3d1b665e
7 changed files with 171 additions and 92 deletions

View file

@ -118,6 +118,7 @@ import cr0s.warpdrive.data.JumpgatesRegistry;
import cr0s.warpdrive.data.StarMapRegistry;
import cr0s.warpdrive.event.ChunkHandler;
import cr0s.warpdrive.event.ClientHandler;
import cr0s.warpdrive.event.CommonWorldGenerator;
import cr0s.warpdrive.event.LivingHandler;
import cr0s.warpdrive.event.WorldHandler;
import cr0s.warpdrive.item.ItemAirTank;
@ -141,9 +142,7 @@ import cr0s.warpdrive.render.RenderOverlayAir;
import cr0s.warpdrive.render.RenderOverlayCamera;
import cr0s.warpdrive.render.RenderOverlayLocation;
import cr0s.warpdrive.world.BiomeSpace;
import cr0s.warpdrive.world.HyperSpaceWorldGenerator;
import cr0s.warpdrive.world.HyperSpaceWorldProvider;
import cr0s.warpdrive.world.SpaceWorldGenerator;
import cr0s.warpdrive.world.SpaceWorldProvider;
import org.apache.logging.log4j.Logger;
@ -274,9 +273,7 @@ public class WarpDrive implements LoadingCallback {
public static BiomeGenBase spaceBiome;
@SuppressWarnings("FieldCanBeLocal")
private SpaceWorldGenerator spaceWorldGenerator;
@SuppressWarnings("FieldCanBeLocal")
private HyperSpaceWorldGenerator hyperSpaceWorldGenerator;
private CommonWorldGenerator commonWorldGenerator;
public static Field fieldBlockHardness = null;
@ -683,10 +680,8 @@ public class WarpDrive implements LoadingCallback {
ForgeChunkManager.setForcedChunkLoadingCallback(instance, instance);
spaceWorldGenerator = new SpaceWorldGenerator();
GameRegistry.registerWorldGenerator(spaceWorldGenerator, 0);
hyperSpaceWorldGenerator = new HyperSpaceWorldGenerator();
GameRegistry.registerWorldGenerator(hyperSpaceWorldGenerator, 0);
commonWorldGenerator = new CommonWorldGenerator();
GameRegistry.registerWorldGenerator(commonWorldGenerator, 0);
spaceBiome = (new BiomeSpace(WarpDriveConfig.G_SPACE_BIOME_ID)).setColor(0).setDisableRain().setBiomeName("Space");
BiomeDictionary.registerBiomeType(spaceBiome, BiomeDictionary.Type.DEAD, BiomeDictionary.Type.WASTELAND);

View file

@ -1,6 +1,7 @@
package cr0s.warpdrive.compat;
import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.api.IBlockTransformer;
import cr0s.warpdrive.api.ITransformation;
import cr0s.warpdrive.block.breathing.BlockAirFlow;
@ -8,6 +9,7 @@ import cr0s.warpdrive.block.breathing.BlockAirSource;
import cr0s.warpdrive.block.energy.TileEntityEnergyBank;
import cr0s.warpdrive.block.hull.BlockHullSlab;
import cr0s.warpdrive.config.WarpDriveConfig;
import cr0s.warpdrive.data.ChunkData;
import cr0s.warpdrive.data.StateAir;
import cr0s.warpdrive.event.ChunkHandler;
@ -15,7 +17,6 @@ import net.minecraft.block.Block;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.IWorldAccess;
import net.minecraft.world.World;
public class CompatWarpDrive implements IBlockTransformer {
@ -39,7 +40,14 @@ public class CompatWarpDrive implements IBlockTransformer {
public NBTBase saveExternals(final World world, final int x, final int y, final int z,
final Block block, final int blockMeta, final TileEntity tileEntity) {
if (block instanceof BlockAirFlow || block instanceof BlockAirSource) {
final int dataAir = ChunkHandler.getChunkData(world, x, y, z).getDataAir(x, y, z);
final ChunkData chunkData = ChunkHandler.getChunkData(world, x, y, z, false);
if (chunkData == null) {
WarpDrive.logger.error(String.format("CompatWarpDrive trying to get data from an non-loaded chunk in %s @ (%d %d %d)",
world.provider.getDimensionName(), x, y, z));
assert(false);
return null;
}
final int dataAir = chunkData.getDataAir(x, y, z);
if (dataAir == StateAir.AIR_DEFAULT) {
return null;
}
@ -115,7 +123,14 @@ public class CompatWarpDrive implements IBlockTransformer {
}
if (((NBTTagCompound) nbtBase).hasKey("dataAir")) {
final int dataAir = ((NBTTagCompound) nbtBase).getInteger("dataAir");
ChunkHandler.getChunkData(world, x, y, z).setDataAir(x, y, z, dataAir);
final ChunkData chunkData = ChunkHandler.getChunkData(world, x, y, z, false);
if (chunkData == null) {
WarpDrive.logger.error(String.format("CompatWarpDrive trying to set data from an non-loaded chunk in %s @ (%d %d %d)",
world.provider.getDimensionName(), x, y, z));
assert(false);
return;
}
chunkData.setDataAir(x, y, z, dataAir);
}
}
}

View file

@ -62,22 +62,22 @@ public class ChunkData {
&& timeUnloaded != 0L
&& time - timeUnloaded < RELOAD_DELAY_MIN_MS ) {
WarpDrive.logger.warn(String.format("Chunk %s (%d %d %d) is reloading after only %d ms",
chunkCoordIntPair,
getChunkPosition().chunkPosX, getChunkPosition().chunkPosY, getChunkPosition().chunkPosZ,
time - timeUnloaded));
chunkCoordIntPair,
getChunkPosition().chunkPosX, getChunkPosition().chunkPosY, getChunkPosition().chunkPosZ,
time - timeUnloaded));
}
// load defaults
Arrays.fill(dataAirSegments, null);
Arrays.fill(tickAirSegments, null);
isModified = false;
// check version
if (nbtTagCompoundChunk.hasKey(TAG_CHUNK_MOD_DATA)) {
final NBTTagCompound nbtTagCompound = nbtTagCompoundChunk.getCompoundTag(TAG_CHUNK_MOD_DATA);
final int version = nbtTagCompound.getInteger(TAG_VERSION);
assert (version == 0 || version == 1);
// load defaults
Arrays.fill(dataAirSegments, null);
Arrays.fill(tickAirSegments, null);
isModified = false;
// load from NBT data
if (version == 1) {
final NBTTagList nbtTagList = nbtTagCompound.getTagList(TAG_AIR, Constants.NBT.TAG_COMPOUND);
@ -142,7 +142,7 @@ public class ChunkData {
// mark as loaded
timeLoaded = time;
isLoaded = true;
if (WarpDrive.isDev && WarpDriveConfig.LOGGING_CHUNK_HANDLER) {
if (WarpDriveConfig.LOGGING_CHUNK_HANDLER) {
WarpDrive.logger.info(String.format("Chunk %s (%d %d %d) is now loaded",
chunkCoordIntPair,
getChunkPosition().chunkPosX, getChunkPosition().chunkPosY, getChunkPosition().chunkPosZ));
@ -496,10 +496,10 @@ public class ChunkData {
@Override
public String toString() {
final ChunkPosition chunkPosition = getChunkPosition();
return String.format("%s (%d %d @ %d %d %d) hasAir %s isNotEmpty %s )",
return String.format("%s (%d %d @ %d %d %d) isLoaded %s hasAir %s isNotEmpty %s )",
getClass().getSimpleName(),
chunkCoordIntPair.chunkXPos, chunkCoordIntPair.chunkZPos,
chunkPosition.chunkPosX, chunkPosition.chunkPosY, chunkPosition.chunkPosZ,
hasAir(), isNotEmpty());
isLoaded, hasAir(), isNotEmpty());
}
}

View file

@ -91,7 +91,12 @@ public class StateAir {
private void refresh(final World world) {
// update chunk cache
if (chunkData == null || !chunkData.isInside(x, y, z)) {
chunkData = ChunkHandler.getChunkData(world, x, y, z);
chunkData = ChunkHandler.getChunkData(world, x, y, z, false);
if (chunkData == null) {
WarpDrive.logger.error(String.format("State air trying to get data from an non-loaded chunk in %s @ (%d %d %d)",
world.provider.getDimensionName(), x, y, z));
assert(false);
}
chunk = null;
}
if (chunk == null) {

View file

@ -23,7 +23,6 @@ import cpw.mods.fml.common.gameevent.TickEvent.WorldTickEvent;
import cpw.mods.fml.relauncher.Side;
import net.minecraftforge.event.world.ChunkDataEvent;
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.event.world.ChunkWatchEvent;
import net.minecraftforge.event.world.WorldEvent;
public class ChunkHandler {
@ -40,48 +39,72 @@ public class ChunkHandler {
/* event catchers */
@SubscribeEvent
public void onLoadWorld(WorldEvent.Load event) {
if (event.world.isRemote || event.world.provider.dimensionId != 0) {
return;
}
if (WarpDriveConfig.LOGGING_CHUNK_HANDLER) {
WarpDrive.logger.info(String.format("World %s load.",
event.world.provider.getDimensionName()));
if (event.world.isRemote || event.world.provider.dimensionId == 0) {
if (WarpDriveConfig.LOGGING_CHUNK_HANDLER) {
WarpDrive.logger.info(String.format("%s world %s load.",
event.world.isRemote ? "Client" : "Server",
event.world.provider.getDimensionName()));
}
}
// load star map
final String filename = String.format("%s/%s.dat", event.world.getSaveHandler().getWorldDirectory().getPath(), WarpDrive.MODID);
final NBTTagCompound tagCompound = Commons.readNBTFromFile(filename);
WarpDrive.starMap.readFromNBT(tagCompound);
if ( !event.world.isRemote
&& event.world.provider.dimensionId == 0 ) {
// load star map
final String filename = String.format("%s/%s.dat", event.world.getSaveHandler().getWorldDirectory().getPath(), WarpDrive.MODID);
final NBTTagCompound tagCompound = Commons.readNBTFromFile(filename);
WarpDrive.starMap.readFromNBT(tagCompound);
}
}
// new chunks aren't loaded
public static void onGenerated(final World world, final int chunkX, final int chunkZ) {
if (WarpDriveConfig.LOGGING_CHUNK_HANDLER) {
WarpDrive.logger.info(String.format("%s world %s chunk [%d, %d] generating",
world.isRemote ? "Client" : "Server",
world.provider.getDimensionName(),
chunkX, chunkZ));
}
final ChunkData chunkData = getChunkData(world.isRemote, world.provider.dimensionId, chunkX, chunkZ, true);
assert(chunkData != null);
// (world can load a non-generated chunk, or the chunk be regenerated, so we reset only as needed)
if (!chunkData.isLoaded()) {
chunkData.load(new NBTTagCompound());
}
}
// (server side only)
@SubscribeEvent
public void onLoadChunkData(ChunkDataEvent.Load event) {
if (WarpDriveConfig.LOGGING_CHUNK_HANDLER) {
WarpDrive.logger.info(String.format("World %s chunk %s loading data",
WarpDrive.logger.info(String.format("%s world %s chunk %s loading data (1)",
event.world.isRemote ? "Client" : "Server",
event.world.provider.getDimensionName(),
event.getChunk().getChunkCoordIntPair()));
}
final ChunkData chunkData = getChunkData(event.world.isRemote, event.world.provider.dimensionId, event.getChunk().xPosition, event.getChunk().zPosition);
final ChunkData chunkData = getChunkData(event.world.isRemote, event.world.provider.dimensionId, event.getChunk().xPosition, event.getChunk().zPosition, true);
assert(chunkData != null);
chunkData.load(event.getData());
}
// (called after data loading, only useful client side)
@SubscribeEvent
public void onLoadChunk(ChunkEvent.Load event) {
if (WarpDriveConfig.LOGGING_CHUNK_HANDLER) {
WarpDrive.logger.info(String.format("%s world %s chunk %s loaded (2)",
event.world.isRemote ? "Client" : "Server",
event.world.provider.getDimensionName(),
event.getChunk().getChunkCoordIntPair()));
}
if (event.world.isRemote) {
if (WarpDriveConfig.LOGGING_CHUNK_HANDLER) {
WarpDrive.logger.info(String.format("World %s chunk %s loaded",
event.world.provider.getDimensionName(),
event.getChunk().getChunkCoordIntPair()));
}
final ChunkData chunkData = getChunkData(event.world.isRemote, event.world.provider.dimensionId, event.getChunk().xPosition, event.getChunk().zPosition);
final ChunkData chunkData = getChunkData(event.world.isRemote, event.world.provider.dimensionId, event.getChunk().xPosition, event.getChunk().zPosition, true);
assert(chunkData != null);
chunkData.load(new NBTTagCompound());
}
}
/*
// (server side only)
@SubscribeEvent
public void onWatchChunk(ChunkWatchEvent.Watch event) {
@ -92,31 +115,44 @@ public class ChunkHandler {
event.player));
}
}
/**/
// (server side only)
// not called when chunk wasn't changed since last save?
@SubscribeEvent
public void onSaveChunkData(ChunkDataEvent.Save event) {
if (WarpDriveConfig.LOGGING_CHUNK_HANDLER) {
WarpDrive.logger.info(String.format("World %s chunk %s save data",
WarpDrive.logger.info(String.format("%s world %s chunk %s save data",
event.world.isRemote ? "Client" : "Server",
event.world.provider.getDimensionName(),
event.getChunk().getChunkCoordIntPair()));
}
final ChunkData chunkData = getChunkData(event.world.isRemote, event.world.provider.dimensionId, event.getChunk().xPosition, event.getChunk().zPosition);
chunkData.save(event.getData());
final ChunkData chunkData = getChunkData(event.world.isRemote, event.world.provider.dimensionId, event.getChunk().xPosition, event.getChunk().zPosition, false);
if (chunkData != null) {
chunkData.save(event.getData());
} else {
WarpDrive.logger.error(String.format("%s world %s chunk %s is saving data without loading it first!",
event.world.isRemote ? "Client" : "Server",
event.world.provider.getDimensionName(),
event.getChunk().getChunkCoordIntPair()));
}
}
// (server side only)
@SubscribeEvent
public void onSaveWorld(WorldEvent.Save event) {
if (event.world.isRemote || event.world.provider.dimensionId != 0) {
if (event.world.provider.dimensionId != 0) {
return;
}
if (WarpDriveConfig.LOGGING_CHUNK_HANDLER) {
WarpDrive.logger.info(String.format("World %s saved.",
WarpDrive.logger.info(String.format("%s world %s saved.",
event.world.isRemote ? "Client" : "Server",
event.world.provider.getDimensionName()));
}
if (event.world.isRemote) {
return;
}
// save star map
final String filename = String.format("%s/%s.dat", event.world.getSaveHandler().getWorldDirectory().getPath(), WarpDrive.MODID);
final NBTTagCompound tagCompound = new NBTTagCompound();
@ -127,7 +163,8 @@ public class ChunkHandler {
@SubscribeEvent
public void onUnloadWorld(WorldEvent.Unload event) {
if (WarpDriveConfig.LOGGING_CHUNK_HANDLER) {
WarpDrive.logger.info(String.format("World %s unload",
WarpDrive.logger.info(String.format("%s world %s unload",
event.world.isRemote ? "Client" : "Server",
event.world.provider.getDimensionName()));
}
@ -144,10 +181,6 @@ public class ChunkHandler {
}
}
if (event.world.isRemote || event.world.provider.dimensionId != 0) {
return;
}
// @TODO unload star map
}
@ -156,25 +189,36 @@ public class ChunkHandler {
@SubscribeEvent
public void onUnloadChunk(ChunkEvent.Unload event) {
if (WarpDriveConfig.LOGGING_CHUNK_HANDLER) {
WarpDrive.logger.info(String.format("World %s chunk %s unload",
WarpDrive.logger.info(String.format("%s world %s chunk %s unload",
event.world.isRemote ? "Client" : "Server",
event.world.provider.getDimensionName(),
event.getChunk().getChunkCoordIntPair()));
}
getChunkData(event.world.isRemote, event.world.provider.dimensionId, event.getChunk().xPosition, event.getChunk().zPosition).unload();
final ChunkData chunkData = getChunkData(event.world.isRemote, event.world.provider.dimensionId, event.getChunk().xPosition, event.getChunk().zPosition, false);
if (chunkData != null) {
chunkData.unload();
} else {
WarpDrive.logger.error(String.format("%s world %s chunk %s is unloading without loading it first!",
event.world.isRemote ? "Client" : "Server",
event.world.provider.getDimensionName(),
event.getChunk().getChunkCoordIntPair()));
}
}
/*
// (not called when closing SSP game)
// warning: will return invalid world when switching dimensions
@SubscribeEvent
public void onUnwatchChunk(ChunkWatchEvent.UnWatch event) {
if (WarpDriveConfig.LOGGING_CHUNK_HANDLER) {
WarpDrive.logger.info(String.format("World %s chunk %s unwatch by %s",
WarpDrive.logger.info(String.format("%s world %s chunk %s unwatch by %s",
event.player.worldObj.isRemote ? "Client" : "Server",
event.player.worldObj.provider.getDimensionName(),
event.chunk,
event.player));
}
}
/**/
@SubscribeEvent
public void onWorldTick(WorldTickEvent event) {
if (event.side != Side.SERVER || event.phase != Phase.END) {
@ -186,21 +230,29 @@ public class ChunkHandler {
public static void onBlockUpdated(final World world, final int x, final int y, final int z) {
if (!world.isRemote) {
getChunkData(world, x, y, z).onBlockUpdated(x, y, z);
final ChunkData chunkData = getChunkData(world, x, y, z, false);
if (chunkData != null) {
chunkData.onBlockUpdated(x, y, z);
} else {
WarpDrive.logger.error(String.format("%s world %s block updating at (%d %d %d), while chunk isn't loaded!",
world.isRemote ? "Client" : "Server",
world.provider.getDimensionName(),
x, y, z));
}
}
}
/* internal access */
public static ChunkData getChunkData(final World world, final int x, final int y, final int z) {
return getChunkData(world.isRemote, world.provider.dimensionId, x, y, z);
public static ChunkData getChunkData(final World world, final int x, final int y, final int z, final boolean doCreate) {
return getChunkData(world.isRemote, world.provider.dimensionId, x, y, z, doCreate);
}
private static ChunkData getChunkData(final boolean isRemote, final int dimensionId, final int x, final int y, final int z) {
private static ChunkData getChunkData(final boolean isRemote, final int dimensionId, final int x, final int y, final int z, final boolean doCreate) {
assert (y >= 0 && y <= 255);
return getChunkData(isRemote, dimensionId, x >> 4, z >> 4);
return getChunkData(isRemote, dimensionId, x >> 4, z >> 4, doCreate);
}
private static ChunkData getChunkData(final boolean isRemote, final int dimensionId, final int xChunk, final int zChunk) {
private static ChunkData getChunkData(final boolean isRemote, final int dimensionId, final int xChunk, final int zChunk, final boolean doCreate) {
// get dimension data
LocalProfiler.updateCallStat("getChunkData");
final Map<Integer, Map<Long, ChunkData>> registry = isRemote ? registryClient : registryServer;
@ -208,6 +260,9 @@ public class ChunkHandler {
// (lambda expressions are forcing synchronisation, so we don't use them here)
//noinspection Java8MapApi
if (mapRegistryItems == null) {
if (!doCreate) {
return null;
}
// TLongObjectMap<ChunkData> m = TCollections.synchronizedMap(new TLongObjectHashMap<ChunkData>(2048) );
// @TODO: http://trove4j.sourceforge.net/javadocs/gnu/trove/TCollections.html#synchronizedMap(gnu.trove.map.TLongObjectMap)
mapRegistryItems = new LinkedHashMap<>(2048); // Collections.synchronizedMap(new LinkedHashMap<>(2048));
@ -219,7 +274,16 @@ public class ChunkHandler {
// (lambda expressions are forcing synchronisation, so we don't use them here)
//noinspection Java8MapApi
if (chunkData == null) {
if (!doCreate) {
return null;
}
chunkData = new ChunkData(xChunk, zChunk);
if (WarpDriveConfig.LOGGING_CHUNK_HANDLER) {
WarpDrive.logger.info(String.format("%s world DIM%d chunk %s is being added to the registry",
isRemote ? "Client" : "Server",
dimensionId,
chunkData.getChunkCoords()));
}
mapRegistryItems.put(index, chunkData);
}
return chunkData;
@ -234,12 +298,13 @@ public class ChunkHandler {
/* commons */
public static boolean isLoaded(final World world, final int x, final int y, final int z) {
return getChunkData(world, x, y, z).isLoaded();
final ChunkData chunkData = getChunkData(world, x, y, z, false);
return chunkData != null && chunkData.isLoaded();
}
/* air handling */
public static StateAir getStateAir(final World world, final int x, final int y, final int z) {
return getChunkData(world, x, y, z).getStateAir(world, x, y, z);
return getChunkData(world, x, y, z, true).getStateAir(world, x, y, z);
}
public static void updateTick(final World world) {
@ -251,15 +316,25 @@ public class ChunkHandler {
return;
}
int countLoaded = 0;
final long time = System.currentTimeMillis() - CHUNK_HANDLER_UNLOADED_CHUNK_MAX_AGE_MS;
for(Iterator<Entry<Long, ChunkData>> entryIterator = mapRegistryItems.entrySet().iterator(); entryIterator.hasNext(); ) {
final long timeForRemoval = System.currentTimeMillis() - CHUNK_HANDLER_UNLOADED_CHUNK_MAX_AGE_MS;
final long timeForThrottle = System.currentTimeMillis() + 200;
for(final Iterator<Entry<Long, ChunkData>> entryIterator = mapRegistryItems.entrySet().iterator(); entryIterator.hasNext(); ) {
final Map.Entry<Long, ChunkData> entryChunkData = entryIterator.next();
final ChunkData chunkData = entryChunkData.getValue();
// update loaded chunks, remove old unloaded chunks
if (chunkData.isLoaded()) {
updateTickLoopStep(world, mapRegistryItems, entryChunkData.getValue());
countLoaded++;
} else if (chunkData.timeUnloaded < time) {
if (System.currentTimeMillis() < timeForThrottle) {
updateTickLoopStep(world, mapRegistryItems, entryChunkData.getValue());
}
} else if (chunkData.timeUnloaded < timeForRemoval) {
if (WarpDriveConfig.LOGGING_CHUNK_HANDLER) {
WarpDrive.logger.info(String.format("%s world %s chunk %s is being removed from updateTick (size is %d)",
world.isRemote ? "Client" : "Server",
world.provider.getDimensionName(),
chunkData.getChunkCoords(),
mapRegistryItems.size()));
}
entryIterator.remove();
}
}

View file

@ -1,4 +1,4 @@
package cr0s.warpdrive.world;
package cr0s.warpdrive.event;
import cr0s.warpdrive.data.CelestialObjectManager;
import cr0s.warpdrive.config.WarpDriveConfig;
@ -7,6 +7,7 @@ import cr0s.warpdrive.config.structures.Orb.OrbShell;
import cr0s.warpdrive.config.structures.OrbInstance;
import cr0s.warpdrive.config.structures.StructureGroup;
import cr0s.warpdrive.data.CelestialObject;
import cr0s.warpdrive.event.ChunkHandler;
import java.util.Random;
@ -15,10 +16,14 @@ import net.minecraft.world.chunk.IChunkProvider;
import cpw.mods.fml.common.IWorldGenerator;
public class SpaceWorldGenerator implements IWorldGenerator {
public class CommonWorldGenerator implements IWorldGenerator {
@Override
public void generate(Random random, int chunkX, int chunkZ, World world, IChunkProvider chunkGenerator, IChunkProvider chunkProvider) {
// chunk data creation
ChunkHandler.onGenerated(world, chunkX, chunkZ);
// actual structure generation
try {
final int x = (chunkX * 16) + (5 - random.nextInt(10));
final int z = (chunkZ * 16) + (5 - random.nextInt(10));
@ -45,6 +50,7 @@ public class SpaceWorldGenerator implements IWorldGenerator {
exception.printStackTrace();
}
}
/**
*
* @deprecated reference design for EntitySphereGenerator

View file

@ -1,17 +0,0 @@
package cr0s.warpdrive.world;
import java.util.Random;
import net.minecraft.world.World;
import net.minecraft.world.chunk.IChunkProvider;
import cpw.mods.fml.common.IWorldGenerator;
public class HyperSpaceWorldGenerator implements IWorldGenerator {
@Override
public void generate(Random random, int chunkX, int chunkZ, World world, IChunkProvider chunkGenerator, IChunkProvider chunkProvider) {
if (world.provider.dimensionId != 0) {
// ...
}
}
}