2012-08-09 23:47:35 +00:00
package net.minecraftforge.common ;
2012-02-25 06:07:58 +00:00
2012-12-20 13:48:14 +00:00
import java.io.File ;
2012-08-15 06:59:15 +00:00
import java.util.ArrayList ;
2013-01-29 01:26:02 +00:00
import java.util.Arrays ;
2012-10-03 05:54:40 +00:00
import java.util.BitSet ;
2013-01-27 15:43:35 +00:00
import java.util.HashSet ;
2012-02-25 06:07:58 +00:00
import java.util.Hashtable ;
2013-01-27 15:43:35 +00:00
import java.util.List ;
import java.util.ListIterator ;
2012-09-22 05:43:54 +00:00
import java.util.Map ;
2012-08-11 01:15:10 +00:00
import java.util.Map.Entry ;
2013-01-27 15:43:35 +00:00
import java.util.Set ;
import java.util.concurrent.ConcurrentMap ;
2012-06-27 21:21:41 +00:00
import java.util.logging.Level ;
2012-09-22 05:43:54 +00:00
import com.google.common.collect.ArrayListMultimap ;
import com.google.common.collect.ImmutableListMultimap ;
import com.google.common.collect.ListMultimap ;
2013-01-27 15:43:35 +00:00
import com.google.common.collect.Lists ;
import com.google.common.collect.MapMaker ;
2012-09-22 05:43:54 +00:00
import com.google.common.collect.Maps ;
2013-01-27 15:43:35 +00:00
import com.google.common.collect.Sets ;
2012-09-22 05:43:54 +00:00
2012-06-27 21:21:41 +00:00
import cpw.mods.fml.common.FMLCommonHandler ;
2012-10-04 04:07:59 +00:00
import cpw.mods.fml.common.FMLLog ;
2012-02-25 06:07:58 +00:00
2012-12-13 07:27:57 +00:00
import net.minecraft.nbt.NBTTagCompound ;
2012-08-11 01:15:10 +00:00
import net.minecraft.server.MinecraftServer ;
2012-12-13 07:27:57 +00:00
import net.minecraft.world.ChunkCoordIntPair ;
import net.minecraft.world.MinecraftException ;
import net.minecraft.world.World ;
import net.minecraft.world.WorldManager ;
import net.minecraft.world.WorldProvider ;
import net.minecraft.world.WorldProviderEnd ;
import net.minecraft.world.WorldProviderHell ;
import net.minecraft.world.WorldProviderSurface ;
import net.minecraft.world.WorldServer ;
import net.minecraft.world.WorldServerMulti ;
import net.minecraft.world.WorldSettings ;
import net.minecraft.world.storage.ISaveHandler ;
2012-12-20 13:48:14 +00:00
import net.minecraft.world.storage.SaveHandler ;
2012-10-02 23:34:50 +00:00
import net.minecraftforge.event.world.WorldEvent ;
2012-02-25 06:07:58 +00:00
public class DimensionManager
{
2012-08-11 01:15:10 +00:00
private static Hashtable < Integer , Class < ? extends WorldProvider > > providers = new Hashtable < Integer , Class < ? extends WorldProvider > > ( ) ;
2012-02-25 06:07:58 +00:00
private static Hashtable < Integer , Boolean > spawnSettings = new Hashtable < Integer , Boolean > ( ) ;
2012-08-11 01:15:10 +00:00
private static Hashtable < Integer , WorldServer > worlds = new Hashtable < Integer , WorldServer > ( ) ;
2012-02-25 06:07:58 +00:00
private static boolean hasInit = false ;
2012-08-11 01:15:10 +00:00
private static Hashtable < Integer , Integer > dimensions = new Hashtable < Integer , Integer > ( ) ;
2012-10-02 23:34:50 +00:00
private static ArrayList < Integer > unloadQueue = new ArrayList < Integer > ( ) ;
2012-10-03 05:54:40 +00:00
private static BitSet dimensionMap = new BitSet ( Long . SIZE < < 4 ) ;
2013-01-27 15:43:35 +00:00
private static ConcurrentMap < World , World > weakWorldMap = new MapMaker ( ) . weakKeys ( ) . weakValues ( ) . < World , World > makeMap ( ) ;
private static Set < Integer > leakedWorlds = Sets . newHashSet ( ) ;
2012-02-25 06:07:58 +00:00
2012-08-11 01:15:10 +00:00
public static boolean registerProviderType ( int id , Class < ? extends WorldProvider > provider , boolean keepLoaded )
2012-02-25 06:07:58 +00:00
{
2013-01-02 04:57:45 +00:00
if ( providers . containsKey ( id ) )
2012-02-25 06:07:58 +00:00
{
return false ;
}
providers . put ( id , provider ) ;
spawnSettings . put ( id , keepLoaded ) ;
return true ;
}
2013-01-29 01:26:02 +00:00
/ * *
2013-04-10 20:37:25 +00:00
* Unregisters a Provider type , and returns a array of all dimensions that are
2013-01-29 01:26:02 +00:00
* registered to this provider type .
2013-04-10 20:37:25 +00:00
* If the return size is greater then 0 , it is required that the caller either
* change those dimensions ' s registered type , or replace this type before the
2013-01-29 01:26:02 +00:00
* world is attempted to load , else the loader will throw an exception .
2013-04-10 20:37:25 +00:00
*
2013-01-29 01:26:02 +00:00
* @param id The provider type ID to unreigster
* @return An array containing all dimension IDs still registered to this provider type .
* /
public static int [ ] unregisterProviderType ( int id )
{
if ( ! providers . containsKey ( id ) )
{
return new int [ 0 ] ;
}
providers . remove ( id ) ;
spawnSettings . remove ( id ) ;
int [ ] ret = new int [ dimensions . size ( ) ] ;
int x = 0 ;
for ( Map . Entry < Integer , Integer > ent : dimensions . entrySet ( ) )
{
if ( ent . getValue ( ) = = id )
{
ret [ x + + ] = ent . getKey ( ) ;
}
}
return Arrays . copyOf ( ret , x ) ;
}
2012-02-25 06:07:58 +00:00
public static void init ( )
{
if ( hasInit )
{
return ;
}
2013-01-02 04:57:45 +00:00
hasInit = true ;
2012-08-11 01:15:10 +00:00
registerProviderType ( 0 , WorldProviderSurface . class , true ) ;
registerProviderType ( - 1 , WorldProviderHell . class , true ) ;
registerProviderType ( 1 , WorldProviderEnd . class , false ) ;
registerDimension ( 0 , 0 ) ;
registerDimension ( - 1 , - 1 ) ;
registerDimension ( 1 , 1 ) ;
2012-02-25 06:07:58 +00:00
}
2012-08-11 01:15:10 +00:00
public static void registerDimension ( int id , int providerType )
2012-02-25 06:07:58 +00:00
{
2012-08-11 01:15:10 +00:00
if ( ! providers . containsKey ( providerType ) )
{
2012-10-02 23:34:50 +00:00
throw new IllegalArgumentException ( String . format ( " Failed to register dimension for id %d, provider type %d does not exist " , id , providerType ) ) ;
2012-08-11 01:15:10 +00:00
}
if ( dimensions . containsKey ( id ) )
{
2012-10-02 23:34:50 +00:00
throw new IllegalArgumentException ( String . format ( " Failed to register dimension for id %d, One is already registered " , id ) ) ;
2012-08-11 01:15:10 +00:00
}
dimensions . put ( id , providerType ) ;
2012-10-03 05:54:40 +00:00
if ( id > = 0 )
{
dimensionMap . set ( id ) ;
}
2012-10-02 23:34:50 +00:00
}
/ * *
* For unregistering a dimension when the save is changed ( disconnected from a server or loaded a new save
* /
public static void unregisterDimension ( int id )
{
if ( ! dimensions . containsKey ( id ) )
{
throw new IllegalArgumentException ( String . format ( " Failed to unregister dimension for id %d; No provider registered " , id ) ) ;
}
dimensions . remove ( id ) ;
2012-08-11 01:15:10 +00:00
}
public static int getProviderType ( int dim )
{
if ( ! dimensions . containsKey ( dim ) )
{
throw new IllegalArgumentException ( String . format ( " Could not get provider type for dimension %d, does not exist " , dim ) ) ;
}
return dimensions . get ( dim ) ;
2012-02-25 06:07:58 +00:00
}
2012-08-14 22:03:40 +00:00
public static WorldProvider getProvider ( int dim )
{
return getWorld ( dim ) . provider ;
}
2013-01-27 15:43:35 +00:00
public static Integer [ ] getIDs ( boolean check )
{
if ( check )
{
List < World > allWorlds = Lists . newArrayList ( weakWorldMap . keySet ( ) ) ;
allWorlds . removeAll ( worlds . values ( ) ) ;
Set < Integer > newLeaks = Sets . newHashSet ( ) ;
for ( ListIterator < World > li = allWorlds . listIterator ( ) ; li . hasNext ( ) ; )
{
World w = li . next ( ) ;
if ( leakedWorlds . contains ( System . identityHashCode ( w ) ) )
{
li . remove ( ) ;
}
newLeaks . add ( System . identityHashCode ( w ) ) ;
}
leakedWorlds = newLeaks ;
if ( allWorlds . size ( ) > 0 )
{
FMLLog . severe ( " Detected leaking worlds in memory. There are %d worlds that appear to be persisting. A mod is likely caching the world incorrectly \ n " , allWorlds . size ( ) + leakedWorlds . size ( ) ) ;
for ( World w : allWorlds )
{
FMLLog . severe ( " The world %x (%s) has leaked. \ n " , System . identityHashCode ( w ) , w . getWorldInfo ( ) . getWorldName ( ) ) ;
}
}
}
return getIDs ( ) ;
}
2012-02-25 06:07:58 +00:00
public static Integer [ ] getIDs ( )
{
2012-10-02 23:34:50 +00:00
return worlds . keySet ( ) . toArray ( new Integer [ worlds . size ( ) ] ) ; //Only loaded dims, since usually used to cycle through loaded worlds
2012-02-25 06:07:58 +00:00
}
2012-08-11 01:15:10 +00:00
public static void setWorld ( int id , WorldServer world )
2012-02-25 06:07:58 +00:00
{
2012-10-02 23:34:50 +00:00
if ( world ! = null ) {
worlds . put ( id , world ) ;
2013-01-27 15:43:35 +00:00
weakWorldMap . put ( world , world ) ;
2012-10-02 23:34:50 +00:00
MinecraftServer . getServer ( ) . worldTickTimes . put ( id , new long [ 100 ] ) ;
2012-10-04 04:07:59 +00:00
FMLLog . info ( " Loading dimension %d (%s) (%s) " , id , world . getWorldInfo ( ) . getWorldName ( ) , world . getMinecraftServer ( ) ) ;
2012-10-02 23:34:50 +00:00
} else {
worlds . remove ( id ) ;
MinecraftServer . getServer ( ) . worldTickTimes . remove ( id ) ;
2012-10-04 04:07:59 +00:00
FMLLog . info ( " Unloading dimension %d " , id ) ;
2012-10-02 23:34:50 +00:00
}
2012-08-11 01:15:10 +00:00
2012-08-15 06:59:15 +00:00
ArrayList < WorldServer > tmp = new ArrayList < WorldServer > ( ) ;
2012-10-02 23:34:50 +00:00
if ( worlds . get ( 0 ) ! = null )
tmp . add ( worlds . get ( 0 ) ) ;
if ( worlds . get ( - 1 ) ! = null )
tmp . add ( worlds . get ( - 1 ) ) ;
if ( worlds . get ( 1 ) ! = null )
tmp . add ( worlds . get ( 1 ) ) ;
2012-08-11 01:15:10 +00:00
for ( Entry < Integer , WorldServer > entry : worlds . entrySet ( ) )
{
int dim = entry . getKey ( ) ;
if ( dim > = - 1 & & dim < = 1 )
{
continue ;
}
2012-08-15 06:59:15 +00:00
tmp . add ( entry . getValue ( ) ) ;
2012-08-11 01:15:10 +00:00
}
2013-01-02 04:57:45 +00:00
MinecraftServer . getServer ( ) . worldServers = tmp . toArray ( new WorldServer [ tmp . size ( ) ] ) ;
2012-10-02 23:34:50 +00:00
}
public static void initDimension ( int dim ) {
WorldServer overworld = getWorld ( 0 ) ;
if ( overworld = = null ) {
throw new RuntimeException ( " Cannot Hotload Dim: Overworld is not Loaded! " ) ;
}
try {
DimensionManager . getProviderType ( dim ) ;
} catch ( Exception e ) {
System . err . println ( " Cannot Hotload Dim: " + e . getMessage ( ) ) ;
return ; //If a provider hasn't been registered then we can't hotload the dim
}
MinecraftServer mcServer = overworld . getMinecraftServer ( ) ;
ISaveHandler savehandler = overworld . getSaveHandler ( ) ;
WorldSettings worldSettings = new WorldSettings ( overworld . getWorldInfo ( ) ) ;
2013-03-20 01:09:34 +00:00
WorldServer world = ( dim = = 0 ? overworld : new WorldServerMulti ( mcServer , savehandler , overworld . getWorldInfo ( ) . getWorldName ( ) , dim , worldSettings , overworld , mcServer . theProfiler , overworld . getWorldLogAgent ( ) ) ) ;
2012-10-02 23:34:50 +00:00
world . addWorldAccess ( new WorldManager ( mcServer , world ) ) ;
2012-10-04 21:35:22 +00:00
MinecraftForge . EVENT_BUS . post ( new WorldEvent . Load ( world ) ) ;
2012-10-02 23:34:50 +00:00
if ( ! mcServer . isSinglePlayer ( ) )
{
world . getWorldInfo ( ) . setGameType ( mcServer . getGameType ( ) ) ;
}
mcServer . setDifficultyForAllWorlds ( mcServer . getDifficulty ( ) ) ;
2012-02-25 06:07:58 +00:00
}
2012-08-11 01:15:10 +00:00
public static WorldServer getWorld ( int id )
2012-02-25 06:07:58 +00:00
{
return worlds . get ( id ) ;
}
2012-08-11 01:15:10 +00:00
public static WorldServer [ ] getWorlds ( )
2012-02-25 06:07:58 +00:00
{
2013-01-02 04:57:45 +00:00
return worlds . values ( ) . toArray ( new WorldServer [ worlds . size ( ) ] ) ;
2012-02-25 06:07:58 +00:00
}
2012-08-11 01:15:10 +00:00
public static boolean shouldLoadSpawn ( int dim )
2012-02-25 06:07:58 +00:00
{
2012-08-11 01:15:10 +00:00
int id = getProviderType ( dim ) ;
2012-12-04 10:01:01 +00:00
return spawnSettings . containsKey ( id ) & & spawnSettings . get ( id ) ;
2012-02-25 06:07:58 +00:00
}
static
{
init ( ) ;
}
2012-06-27 21:21:41 +00:00
2012-10-04 04:07:59 +00:00
/ * *
* Not public API : used internally to get dimensions that should load at
* server startup
* /
public static Integer [ ] getStaticDimensionIDs ( )
{
return dimensions . keySet ( ) . toArray ( new Integer [ dimensions . keySet ( ) . size ( ) ] ) ;
}
2012-08-11 01:15:10 +00:00
public static WorldProvider createProviderFor ( int dim )
{
try
{
if ( dimensions . containsKey ( dim ) )
{
2012-08-15 06:59:15 +00:00
WorldProvider provider = providers . get ( getProviderType ( dim ) ) . newInstance ( ) ;
provider . setDimension ( dim ) ;
return provider ;
2012-08-11 01:15:10 +00:00
}
else
{
2012-10-02 23:34:50 +00:00
throw new RuntimeException ( String . format ( " No WorldProvider bound for dimension %d " , dim ) ) ; //It's going to crash anyway at this point. Might as well be informative
2012-08-11 01:15:10 +00:00
}
2012-09-22 05:43:54 +00:00
}
2012-08-11 01:15:10 +00:00
catch ( Exception e )
{
FMLCommonHandler . instance ( ) . getFMLLogger ( ) . log ( Level . SEVERE , String . format ( " An error occured trying to create an instance of WorldProvider %d (%s) " ,
2012-09-22 05:43:54 +00:00
dim , providers . get ( getProviderType ( dim ) ) . getSimpleName ( ) ) , e ) ;
2012-08-11 01:15:10 +00:00
throw new RuntimeException ( e ) ;
}
}
2012-10-02 23:34:50 +00:00
public static void unloadWorld ( int id ) {
unloadQueue . add ( id ) ;
}
/ *
* To be called by the server at the appropriate time , do not call from mod code .
* /
public static void unloadWorlds ( Hashtable < Integer , long [ ] > worldTickTimes ) {
for ( int id : unloadQueue ) {
2013-01-24 04:36:59 +00:00
WorldServer w = worlds . get ( id ) ;
2012-10-02 23:34:50 +00:00
try {
2013-01-24 04:36:59 +00:00
if ( w ! = null )
{
w . saveAllChunks ( true , null ) ;
}
else
{
FMLLog . warning ( " Unexpected world unload - world %d is already unloaded " , id ) ;
}
2012-10-02 23:34:50 +00:00
} catch ( MinecraftException e ) {
e . printStackTrace ( ) ;
}
2013-01-24 04:36:59 +00:00
finally
{
if ( w ! = null )
{
MinecraftForge . EVENT_BUS . post ( new WorldEvent . Unload ( w ) ) ;
w . flush ( ) ;
setWorld ( id , null ) ;
}
}
2012-10-02 23:34:50 +00:00
}
unloadQueue . clear ( ) ;
}
2012-10-03 05:54:40 +00:00
/ * *
* Return the next free dimension ID . Note : you are not guaranteed a contiguous
* block of free ids . Always call for each individual ID you wish to get .
2013-01-22 02:56:04 +00:00
* @return the next free dimension ID
2012-10-03 05:54:40 +00:00
* /
2012-10-02 23:34:50 +00:00
public static int getNextFreeDimId ( ) {
2012-10-03 05:54:40 +00:00
int next = 0 ;
while ( true )
{
next = dimensionMap . nextClearBit ( next ) ;
if ( dimensions . containsKey ( next ) )
{
dimensionMap . set ( next ) ;
}
else
{
return next ;
}
}
}
public static NBTTagCompound saveDimensionDataMap ( )
{
int [ ] data = new int [ ( dimensionMap . length ( ) + Integer . SIZE - 1 ) / Integer . SIZE ] ;
NBTTagCompound dimMap = new NBTTagCompound ( ) ;
for ( int i = 0 ; i < data . length ; i + + )
{
int val = 0 ;
for ( int j = 0 ; j < Integer . SIZE ; j + + )
{
val | = dimensionMap . get ( i * Integer . SIZE + j ) ? ( 1 < < j ) : 0 ;
}
data [ i ] = val ;
}
dimMap . setIntArray ( " DimensionArray " , data ) ;
return dimMap ;
}
public static void loadDimensionDataMap ( NBTTagCompound compoundTag )
{
if ( compoundTag = = null )
{
dimensionMap . clear ( ) ;
for ( Integer id : dimensions . keySet ( ) )
{
if ( id > = 0 )
{
dimensionMap . set ( id ) ;
}
}
}
else
{
int [ ] intArray = compoundTag . getIntArray ( " DimensionArray " ) ;
for ( int i = 0 ; i < intArray . length ; i + + )
{
for ( int j = 0 ; j < Integer . SIZE ; j + + )
{
dimensionMap . set ( i * Integer . SIZE + j , ( intArray [ i ] & ( 1 < < j ) ) ! = 0 ) ;
}
}
}
2012-10-02 23:34:50 +00:00
}
2012-12-20 13:48:14 +00:00
/ * *
2013-01-22 02:56:04 +00:00
* Return the current root directory for the world save . Accesses getSaveHandler from the overworld
* @return the root directory of the save
2012-12-20 13:48:14 +00:00
* /
public static File getCurrentSaveRootDirectory ( )
{
if ( DimensionManager . getWorld ( 0 ) ! = null )
{
2013-04-10 20:37:25 +00:00
return ( ( SaveHandler ) DimensionManager . getWorld ( 0 ) . getSaveHandler ( ) ) . getWorldDirectory ( ) ;
2012-12-20 13:48:14 +00:00
}
2013-01-22 15:07:41 +00:00
else if ( MinecraftServer . getServer ( ) ! = null )
{
MinecraftServer srv = MinecraftServer . getServer ( ) ;
SaveHandler saveHandler = ( SaveHandler ) srv . getActiveAnvilConverter ( ) . getSaveLoader ( srv . getFolderName ( ) , false ) ;
2013-04-10 20:37:25 +00:00
return saveHandler . getWorldDirectory ( ) ;
2013-01-22 15:07:41 +00:00
}
2012-12-20 13:48:14 +00:00
else
{
return null ;
}
}
2012-02-25 06:07:58 +00:00
}